Explorar el Código

Merge branch 'next' of gitee.com:KaneLeung/Admin.NET into next

KaneLeung hace 1 año
padre
commit
f0f5707701
Se han modificado 100 ficheros con 3375 adiciones y 1399 borrados
  1. 0 1
      Admin.NET/Admin.NET.Application/Admin.NET.Application.csproj
  2. 8 2
      Admin.NET/Admin.NET.Application/Configuration/Database.json
  3. 1 1
      Admin.NET/Admin.NET.Application/Configuration/Enum.json
  4. 22 4
      Admin.NET/Admin.NET.Application/Configuration/SMS.json
  5. 2 2
      Admin.NET/Admin.NET.Application/Configuration/Upload.json
  6. 15 15
      Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
  7. 69 0
      Admin.NET/Admin.NET.Core/Attribute/DictAttribute.cs
  8. 25 0
      Admin.NET/Admin.NET.Core/Attribute/SeedDataAttribute.cs
  9. 45 0
      Admin.NET/Admin.NET.Core/Attribute/ThemeAttribute.cs
  10. 38 32
      Admin.NET/Admin.NET.Core/Cache/CacheSetup.cs
  11. 6 1
      Admin.NET/Admin.NET.Core/Const/CacheConst.cs
  12. 15 0
      Admin.NET/Admin.NET.Core/Const/CommonConst.cs
  13. 1 1
      Admin.NET/Admin.NET.Core/Entity/EntityBase.cs
  14. 1 1
      Admin.NET/Admin.NET.Core/Entity/SysConfig.cs
  15. 4 4
      Admin.NET/Admin.NET.Core/Entity/SysDictData.cs
  16. 0 2
      Admin.NET/Admin.NET.Core/Entity/SysOpenAccess.cs
  17. 1 1
      Admin.NET/Admin.NET.Core/Entity/SysOrg.cs
  18. 1 1
      Admin.NET/Admin.NET.Core/Entity/SysPrint.cs
  19. 190 164
      Admin.NET/Admin.NET.Core/Entity/SysWechatPay.cs
  20. 97 0
      Admin.NET/Admin.NET.Core/Entity/SysWechatRefund.cs
  21. 9 3
      Admin.NET/Admin.NET.Core/Enum/ErrorCodeEnum.cs
  22. 9 11
      Admin.NET/Admin.NET.Core/EventBus/EventConsumer.cs
  23. 31 0
      Admin.NET/Admin.NET.Core/EventBus/EventHandlerMonitor.cs
  24. 15 15
      Admin.NET/Admin.NET.Core/EventBus/RedisEventSourceStorer.cs
  25. 48 107
      Admin.NET/Admin.NET.Core/EventBus/RedisQueue.cs
  26. 43 21
      Admin.NET/Admin.NET.Core/EventBus/RetryEventHandlerExecutor.cs
  27. 18 1
      Admin.NET/Admin.NET.Core/Extension/EnumExtension.cs
  28. 2 1
      Admin.NET/Admin.NET.Core/Job/EnumToDictJob.cs
  29. 3 2
      Admin.NET/Admin.NET.Core/Job/LogJob.cs
  30. 12 28
      Admin.NET/Admin.NET.Core/Logging/DatabaseLoggingWriter.cs
  31. 2 2
      Admin.NET/Admin.NET.Core/Logging/ElasticSearchLoggingWriter.cs
  32. 18 3
      Admin.NET/Admin.NET.Core/Option/SMSOptions.cs
  33. 19 16
      Admin.NET/Admin.NET.Core/SeedData/SysConfigSeedData.cs
  34. 205 195
      Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs
  35. 5 0
      Admin.NET/Admin.NET.Core/Service/Auth/Dto/LoginUserOutput.cs
  36. 34 29
      Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs
  37. 13 21
      Admin.NET/Admin.NET.Core/Service/Auth/SysLdapService.cs
  38. 23 23
      Admin.NET/Admin.NET.Core/Service/Cache/SysCacheService.cs
  39. 1 1
      Admin.NET/Admin.NET.Core/Service/CodeGen/SysCodeGenConfigService.cs
  40. 9 4
      Admin.NET/Admin.NET.Core/Service/CodeGen/SysCodeGenService.cs
  41. 16 0
      Admin.NET/Admin.NET.Core/Service/Config/Dto/ConfigInput.cs
  42. 39 27
      Admin.NET/Admin.NET.Core/Service/Config/SysConfigService.cs
  43. 2 1
      Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs
  44. 46 11
      Admin.NET/Admin.NET.Core/Service/Dict/SysDictDataService.cs
  45. 11 1
      Admin.NET/Admin.NET.Core/Service/Dict/SysDictTypeService.cs
  46. 4 1
      Admin.NET/Admin.NET.Core/Service/Enum/SysEnumService.cs
  47. 16 30
      Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs
  48. 11 8
      Admin.NET/Admin.NET.Core/Service/Job/DbJobPersistence.cs
  49. 106 106
      Admin.NET/Admin.NET.Core/Service/Job/JobClusterServer.cs
  50. 47 6
      Admin.NET/Admin.NET.Core/Service/Message/SysSmsService.cs
  51. 9 16
      Admin.NET/Admin.NET.Core/Service/OnlineUser/SysOnlineUserService.cs
  52. 6 0
      Admin.NET/Admin.NET.Core/Service/Org/Dto/OrgInput.cs
  53. 25 32
      Admin.NET/Admin.NET.Core/Service/Org/SysOrgService.cs
  54. 15 19
      Admin.NET/Admin.NET.Core/Service/Role/SysRoleOrgService.cs
  55. 1 0
      Admin.NET/Admin.NET.Core/Service/User/Dto/UserInput.cs
  56. 10 18
      Admin.NET/Admin.NET.Core/Service/User/SysUserExtOrgService.cs
  57. 10 18
      Admin.NET/Admin.NET.Core/Service/User/SysUserLdapService.cs
  58. 12 19
      Admin.NET/Admin.NET.Core/Service/User/SysUserRoleService.cs
  59. 29 32
      Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs
  60. 47 1
      Admin.NET/Admin.NET.Core/Service/Wechat/Dto/WechatPayInput.cs
  61. 25 1
      Admin.NET/Admin.NET.Core/Service/Wechat/Dto/WechatPayOutput.cs
  62. 315 46
      Admin.NET/Admin.NET.Core/Service/Wechat/SysWechatPayService.cs
  63. 1 1
      Admin.NET/Admin.NET.Core/Service/Wechat/SysWechatService.cs
  64. 15 17
      Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs
  65. 1 1
      Admin.NET/Admin.NET.Core/Util/CodeGenUtil.cs
  66. 68 24
      Admin.NET/Admin.NET.Core/Util/CommonUtil.cs
  67. 10 3
      Admin.NET/Admin.NET.Core/Util/ComputerUtil.cs
  68. 7 6
      Admin.NET/Admin.NET.Web.Core/Handlers/JwtHandler.cs
  69. 36 9
      Admin.NET/Admin.NET.Web.Core/Startup.cs
  70. 2 2
      Admin.NET/Admin.NET.Web.Entry/Program.cs
  71. 27 31
      Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/Service.cs.vm
  72. 24 26
      Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/editDialog.vue.vm
  73. 17 24
      Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/index.vue.vm
  74. 1 1
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Middleware/ApprovalFlowMiddleware.cs
  75. 13 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Const/DingTalkConst.cs
  76. 19 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkUser.cs
  77. 13 1
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Job/SyncDingTalkUserJob.cs
  78. 2 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkCurrentEmployeesRosterListInput.cs
  79. 2 1
      Admin.NET/Plugins/Admin.NET.Plugin.GoView/Entity/GoViewPro.cs
  80. 8 1
      Admin.NET/Plugins/Admin.NET.Plugin.GoView/Entity/GoViewProData.cs
  81. 6 1
      Admin.NET/Plugins/Admin.NET.Plugin.GoView/Service/GoViewPro/Dto/GoViewProOutput.cs
  82. 72 1
      Admin.NET/Plugins/Admin.NET.Plugin.GoView/Service/GoViewPro/GoViewProService.cs
  83. 4 4
      Admin.NET/Plugins/Admin.NET.Plugin.GoView/Service/GoViewSys/GoViewSysService.cs
  84. 2 2
      Admin.NET/Plugins/Admin.NET.Plugin.ReZero/Admin.NET.Plugin.ReZero.csproj
  85. 0 0
      Web/.prettierrc.cjs
  86. 25 21
      Web/package.json
  87. 0 1
      Web/src/App.vue
  88. 0 75
      Web/src/api-services/apis/sys-auth-api.ts
  89. 100 8
      Web/src/api-services/apis/sys-config-api.ts
  90. 96 0
      Web/src/api-services/apis/sys-sms-api.ts
  91. 529 22
      Web/src/api-services/apis/sys-wechat-pay-api.ts
  92. 8 8
      Web/src/api-services/models/add-org-input.ts
  93. 71 0
      Web/src/api-services/models/admin-result-list-sys-wechat-refund.ts
  94. 69 0
      Web/src/api-services/models/admin-result-sql-sugar-paged-list-sys-wechat-pay.ts
  95. 69 0
      Web/src/api-services/models/admin-result-sys-wechat-refund.ts
  96. 69 0
      Web/src/api-services/models/admin-result-wechat-pay-para-output.ts
  97. 69 0
      Web/src/api-services/models/admin-result-wechat-pay-transaction-output.ts
  98. 38 0
      Web/src/api-services/models/batch-config-input.ts
  99. 8 0
      Web/src/api-services/models/enum-entity.ts
  100. 12 0
      Web/src/api-services/models/index.ts

+ 0 - 1
Admin.NET/Admin.NET.Application/Admin.NET.Application.csproj

@@ -29,7 +29,6 @@
     <ProjectReference Include="..\Plugins\Admin.NET.Plugin.ApprovalFlow\Admin.NET.Plugin.ApprovalFlow.csproj" />
     <ProjectReference Include="..\Plugins\Admin.NET.Plugin.DingTalk\Admin.NET.Plugin.DingTalk.csproj" />
     <ProjectReference Include="..\Plugins\Admin.NET.Plugin.GoView\Admin.NET.Plugin.GoView.csproj" />
-    <ProjectReference Include="..\Plugins\Admin.NET.Plugin.ReZero\Admin.NET.Plugin.ReZero.csproj" />
   </ItemGroup>
 
 </Project>

+ 8 - 2
Admin.NET/Admin.NET.Application/Configuration/Database.json

@@ -1,14 +1,20 @@
 {
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
-  // 详细数据库配置见SqlSugar官网(第一个为默认库)
+  // 详细数据库配置见SqlSugar官网(第一个为默认库),极力推荐 PostgreSQL 数据库
+  // 数据库连接字符串参考地址:https://www.connectionstrings.com/
   "DbConnection": {
     "EnableConsoleSql": true, // 启用控制台打印SQL
     "ConnectionConfigs": [
       {
         //"ConfigId": "1300000000001", // 默认库标识-禁止修改
         "DbType": "Sqlite", // MySql、SqlServer、Sqlite、Oracle、PostgreSQL、Dm、Kdbndp、Oscar、MySqlConnector、Access、OpenGauss、QuestDB、HG、ClickHouse、GBase、Odbc、Custom
-        "ConnectionString": "DataSource=./Admin.NET.db", // 库连接字符串
+        "ConnectionString": "DataSource=./Admin.NET.db", // Sqlite 库连接字符串
+        //"ConnectionString": "PORT=5432;DATABASE=xxx;HOST=localhost;PASSWORD=xxx;USER ID=xxx", // PostgreSQL 库连接字符串
+        //"ConnectionString": "Server=localhost;Database=xxx;Uid=xxx;Pwd=xxx;SslMode=None;", // MySql 库连接字符串",
+        //"ConnectionString": "User Id=xxx; Password=xxx; Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ORCL)))", // Oracle 库连接字符串
+        //"ConnectionString": "Server=localhost;Database=xxx;User Id=xxx;Password=xxx;", // SqlServer 库连接字符串
+
         //"SlaveConnectionConfigs": [ // 读写分离/主从
         //	{
         //		"HitRate": 10,

+ 1 - 1
Admin.NET/Admin.NET.Application/Configuration/Enum.json

@@ -3,6 +3,6 @@
 
   // 枚举实体所在程序集名称集合
   "Enum": {
-    "EntityAssemblyNames": [ "Admin.NET.Core", "Admin.NET.Application", "Admin.NET.AppCMS" ]
+    "EntityAssemblyNames": [ "Admin." ]
   }
 }

+ 22 - 4
Admin.NET/Admin.NET.Application/Configuration/SMS.json

@@ -5,15 +5,33 @@
     "Aliyun": {
       "AccessKeyId": "",
       "AccessKeySecret": "",
-      "SignName": "AdminNET 平台", // 短信签名
-      "TemplateCode": "" // 短信模板
+      "Templates": [
+        {
+          "Id": "0",
+          "SignName": "AdminNET 平台",
+          "TemplateCode": "SMS_291005708",
+          "Content": "您的验证码为:${code},请勿泄露于他人!"
+        },
+        {
+          "Id": "1",
+          "SignName": "AdminNET 平台",
+          "TemplateCode": "SMS_462801755",
+          "Content": "注册成功,感谢您的注册,请妥善保管您的账户信息"
+        }
+      ]
     },
     "Tencentyun": {
       "SdkAppId": "",
       "AccessKeyId": "",
       "AccessKeySecret": "",
-      "SignName": "AdminNET 平台", // 短信签名
-      "TemplateCode": "" // 短信模板
+      "Templates": [
+        {
+          "Id": "0",
+          "SignName": "AdminNET 平台",
+          "TemplateCode": "",
+          "Content": ""
+        }
+      ]
     }
   }
 }

+ 2 - 2
Admin.NET/Admin.NET.Application/Configuration/Upload.json

@@ -3,8 +3,8 @@
 
   "Upload": {
     "Path": "Upload/{yyyy}/{MM}/{dd}", // 文件上传目录
-    "MaxSize": 20480, // 文件最大限制KB:1024*20
-    "ContentType": [ "image/jpg", "image/png", "image/jpeg", "image/gif", "image/bmp", "text/plain", "application/pdf", "application/msword", "application/vnd.ms-excel", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "video/mp4", "application/wps-office.docx", "application/wps-office.xlsx" ],
+    "MaxSize": 51200, // 文件最大限制KB:1024*50
+    "ContentType": [ "image/jpg", "image/png", "image/jpeg", "image/gif", "image/bmp", "text/plain", "application/pdf", "application/msword", "application/vnd.ms-excel", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "video/mp4", "application/wps-office.docx", "application/wps-office.xlsx", "application/wps-office.pptx" ],
     "EnableMd5": false // 启用文件MDF5验证-防止重复上传
   },
   "OSSProvider": {

+ 15 - 15
Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj

@@ -13,31 +13,31 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="2.0.24" />
+    <PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="3.0.0" />
     <PackageReference Include="AngleSharp" Version="1.1.2" />
     <PackageReference Include="AspectCore.Extensions.Reflection" Version="2.4.0" />
     <PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
-    <PackageReference Include="Elastic.Clients.Elasticsearch" Version="8.14.0" />
-    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.3.19" />
-    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.3.19" />
-    <PackageReference Include="Furion.Pure" Version="4.9.3.19" />
+    <PackageReference Include="Elastic.Clients.Elasticsearch" Version="8.14.4" />
+    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.4.3" />
+    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.4.3" />
+    <PackageReference Include="Furion.Pure" Version="4.9.4.3" />
     <PackageReference Include="IPTools.China" Version="1.6.0" />
     <PackageReference Include="IPTools.International" Version="1.6.0" />
     <PackageReference Include="Magicodes.IE.Excel" Version="2.7.5.1" />
     <PackageReference Include="Magicodes.IE.Pdf" Version="2.7.5.1" />
     <PackageReference Include="Magicodes.IE.Word" Version="2.7.5.1" />
-    <PackageReference Include="MailKit" Version="4.6.0" />
-    <PackageReference Include="NewLife.Redis" Version="5.7.2024.602" />
+    <PackageReference Include="MailKit" Version="4.7.0" />
+    <PackageReference Include="NewLife.Redis" Version="5.7.2024.701" />
     <PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0" />
-    <PackageReference Include="QRCoder" Version="1.5.1" />
+    <PackageReference Include="QRCoder" Version="1.6.0" />
     <PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
     <PackageReference Include="SixLabors.ImageSharp.Web" Version="3.1.2" />
     <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.3.0" />
-    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.4.0" />
-    <PackageReference Include="SqlSugarCore" Version="5.1.4.158" />
-    <PackageReference Include="SSH.NET" Version="2024.0.0" />
-    <PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.0" />
-    <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1022" />
+    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.6.0" />
+    <PackageReference Include="SqlSugarCore" Version="5.1.4.160" />
+    <PackageReference Include="SSH.NET" Version="2024.1.0" />
+    <PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.3" />
+    <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1040" />
     <PackageReference Include="UAParser" Version="3.1.47" />
     <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
   </ItemGroup>
@@ -53,8 +53,8 @@
   </ItemGroup>
 
   <ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
-    <PackageReference Include="AspNet.Security.OAuth.Gitee" Version="8.0.0" />
-    <PackageReference Include="AspNet.Security.OAuth.Weixin" Version="8.0.0" />
+    <PackageReference Include="AspNet.Security.OAuth.Gitee" Version="8.1.0" />
+    <PackageReference Include="AspNet.Security.OAuth.Weixin" Version="8.1.0" />
     <PackageReference Include="Lazy.Captcha.Core" Version="2.0.7" />
     <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="8.0.6" />
     <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="8.0.6" />

+ 69 - 0
Admin.NET/Admin.NET.Core/Attribute/DictAttribute.cs

@@ -0,0 +1,69 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 字典值合规性校验特性
+/// </summary>
+[SuppressSniffer]
+[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = true)]
+public class DictAttribute : ValidationAttribute, ITransient
+{
+    /// <summary>
+    /// 字典值合规性校验特性
+    /// </summary>
+    /// <param name="dictTypeCode"></param>
+    /// <param name="errorMessage"></param>
+    public DictAttribute(string dictTypeCode, string errorMessage = "字典值不合法!")
+    {
+        DictTypeCode = dictTypeCode;
+        ErrorMessage = errorMessage;
+    }
+
+    /// <summary>
+    /// 字典值合规性校验
+    /// </summary>
+    /// <param name="value"></param>
+    /// <param name="validationContext"></param>
+    /// <returns></returns>
+    protected override ValidationResult IsValid(object? value, ValidationContext validationContext)
+    {
+        var valueAsString = value?.ToString();
+
+        // 判断是否允许空值
+        if (AllowNullValue && value == null) return ValidationResult.Success;
+
+        // 是否忽略空字符串
+        if (AllowEmptyStrings && string.IsNullOrEmpty(valueAsString)) return ValidationResult.Success;
+
+        var sysDictDataServiceProvider = validationContext.GetRequiredService<SysDictDataService>();
+        var dictDataList = sysDictDataServiceProvider.GetDataList(DictTypeCode).Result;
+
+        // 使用HashSet来提高查找效率
+        var dictCodes = new HashSet<string>(dictDataList.Select(u => u.Code));
+
+        if (!dictCodes.Contains(valueAsString))
+            return new ValidationResult($"提示:{ErrorMessage}|字典【{DictTypeCode}】不包含【{valueAsString}】!");
+        else
+            return ValidationResult.Success;
+    }
+
+    /// <summary>
+    /// 字典编码
+    /// </summary>
+    public string DictTypeCode { get; set; }
+
+    /// <summary>
+    /// 是否允许空字符串
+    /// </summary>
+    public bool AllowEmptyStrings { get; set; } = false;
+
+    /// <summary>
+    /// 允许空值,有值才验证,默认 false
+    /// </summary>
+    public bool AllowNullValue { get; set; } = false;
+}

+ 25 - 0
Admin.NET/Admin.NET.Core/Attribute/SeedDataAttribute.cs

@@ -0,0 +1,25 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 种子数据特性
+/// </summary>
+[SuppressSniffer]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
+public class SeedDataAttribute : Attribute
+{
+    /// <summary>
+    /// 排序(越大越后执行)
+    /// </summary>
+    public int Order { get; set; } = 0;
+
+    public SeedDataAttribute(int orderNo)
+    {
+        Order = orderNo;
+    }
+}

+ 45 - 0
Admin.NET/Admin.NET.Core/Attribute/ThemeAttribute.cs

@@ -0,0 +1,45 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 枚举拓展主题样式
+/// </summary>
+[SuppressSniffer]
+[AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field)]
+public class ThemeAttribute : Attribute
+{
+    public string Theme { get; private set; }
+
+    public ThemeAttribute(string theme)
+    {
+        this.Theme = theme;
+    }
+}
+
+public static class ThemeExtend
+{
+    /// <summary>
+    /// 扩展方法
+    /// </summary>
+    /// <param name="enumValue"></param>
+    /// <returns></returns>
+    public static string GetTheme(this Enum enumValue)
+    {
+        Type type = enumValue.GetType();
+        FieldInfo field = type.GetField(enumValue.ToString());
+        if (field.IsDefined(typeof(ThemeAttribute), true))
+        {
+            var themeAttribute = (ThemeAttribute)field.GetCustomAttribute(typeof(ThemeAttribute));
+            return themeAttribute.Theme;
+        }
+        else
+        {
+            return string.Empty;
+        }
+    }
+}

+ 38 - 32
Admin.NET/Admin.NET.Core/Cache/CacheSetup.cs

@@ -1,33 +1,39 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Core;
-
-public static class CacheSetup
-{
-    /// <summary>
-    /// 缓存注册(新生命Redis组件)
-    /// </summary>
-    /// <param name="services"></param>
-    public static void AddCache(this IServiceCollection services)
-    {
-        ICache cache = Cache.Default;
-
-        var cacheOptions = App.GetConfig<CacheOptions>("Cache", true);
-        if (cacheOptions.CacheType == CacheTypeEnum.Redis.ToString())
-        {
-            cache = new FullRedis(new RedisOptions
-            {
-                Configuration = cacheOptions.Redis.Configuration,
-                Prefix = cacheOptions.Redis.Prefix
-            });
-            if (cacheOptions.Redis.MaxMessageSize > 0)
-                ((FullRedis)cache).MaxMessageSize = cacheOptions.Redis.MaxMessageSize;
-        }
-
-        services.AddSingleton(cache);
-    }
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using NewLife.Caching;
+using NewLife.Caching.Services;
+
+namespace Admin.NET.Core;
+
+public static class CacheSetup
+{
+    /// <summary>
+    /// 缓存注册(新生命Redis组件)
+    /// </summary>
+    /// <param name="services"></param>
+    public static void AddCache(this IServiceCollection services)
+    {
+        var cacheOptions = App.GetConfig<CacheOptions>("Cache", true);
+        if (cacheOptions.CacheType == CacheTypeEnum.Redis.ToString())
+        {
+            var redis = new FullRedis(new RedisOptions
+            {
+                Configuration = cacheOptions.Redis.Configuration,
+                Prefix = cacheOptions.Redis.Prefix
+            });
+            if (cacheOptions.Redis.MaxMessageSize > 0)
+                redis.MaxMessageSize = cacheOptions.Redis.MaxMessageSize;
+
+            // 注入 Redis 缓存提供者
+            services.AddSingleton<ICacheProvider>(p => new RedisCacheProvider(p) { Cache = redis });
+        }
+
+        // 内存缓存兜底。在没有配置Redis时,使用内存缓存,逻辑代码无需修改
+        services.TryAddSingleton<ICacheProvider, CacheProvider>();
+    }
 }

+ 6 - 1
Admin.NET/Admin.NET.Core/Const/CacheConst.cs

@@ -44,7 +44,7 @@ public class CacheConst
     /// <summary>
     /// 密码错误次数缓存
     /// </summary>
-    public const string KeyErrorPasswordCount = "sys_errorPasswordCount:";
+    public const string KeyPasswordErrorTimes = "sys_password_error_times:";
 
     /// <summary>
     /// 租户缓存
@@ -85,4 +85,9 @@ public class CacheConst
     /// 系统配置缓存
     /// </summary>
     public const string KeyConfig = "sys_config:";
+
+    /// <summary>
+    /// 系统字典缓存
+    /// </summary>
+    public const string KeyDict = "sys_dict:";
 }

+ 15 - 0
Admin.NET/Admin.NET.Core/Const/CommonConst.cs

@@ -22,6 +22,11 @@ public class CommonConst
     /// </summary>
     public const string SysPassword = "sys_password";
 
+    /// <summary>
+    /// 密码最大错误次数
+    /// </summary>
+    public const string SysPasswordMaxErrorTimes = "sys_password_max_error_times";
+
     /// <summary>
     /// 登录二次验证
     /// </summary>
@@ -77,6 +82,16 @@ public class CommonConst
     /// </summary>
     public const string SysDomainLogin = "sys_domain_login";
 
+    /// <summary>
+    /// 开启记录数据校验日志
+    /// </summary>
+    public const string SysValidationLog = "sys_validation_log";
+
+    /// <summary>
+    /// 日志保留天数
+    /// </summary>
+    public const string SysLogRetentionDays = "sys_log_retention_days";
+
     /// <summary>
     /// 日志分组名称
     /// </summary>

+ 1 - 1
Admin.NET/Admin.NET.Core/Entity/EntityBase.cs

@@ -27,7 +27,7 @@ public abstract class EntityBase : EntityBaseId, IDeletedFilter
     /// <summary>
     /// 创建时间
     /// </summary>
-    [SugarColumn(ColumnDescription = "创建时间", IsOnlyIgnoreUpdate = true, InsertServerTime = true)]
+    [SugarColumn(ColumnDescription = "创建时间", IsNullable = true, IsOnlyIgnoreUpdate = true, InsertServerTime = true)]
     public virtual DateTime CreateTime { get; set; }
 
     /// <summary>

+ 1 - 1
Admin.NET/Admin.NET.Core/Entity/SysConfig.cs

@@ -12,7 +12,7 @@ namespace Admin.NET.Core;
 [SugarTable(null, "系统参数配置表")]
 [SysTable]
 [SugarIndex("index_{table}_N", nameof(Name), OrderByType.Asc)]
-[SugarIndex("index_{table}_C", nameof(Code), OrderByType.Asc)]
+[SugarIndex("index_{table}_C", nameof(Code), OrderByType.Asc, IsUnique = true)]
 public partial class SysConfig : EntityBase
 {
     /// <summary>

+ 4 - 4
Admin.NET/Admin.NET.Core/Entity/SysDictData.cs

@@ -38,15 +38,15 @@ public partial class SysDictData : EntityBase
     /// <summary>
     /// 编码
     /// </summary>
-    [SugarColumn(ColumnDescription = "编码", Length = 128)]
-    [Required, MaxLength(128)]
+    [SugarColumn(ColumnDescription = "编码", Length = 256)]
+    [Required, MaxLength(256)]
     public virtual string Code { get; set; }
 
     /// <summary>
     /// 名称
     /// </summary>
-    [SugarColumn(ColumnDescription = "名称", Length = 128)]
-    [MaxLength(128)]
+    [SugarColumn(ColumnDescription = "名称", Length = 256)]
+    [MaxLength(256)]
     public virtual string? Name { get; set; }
 
     /// <summary>

+ 0 - 2
Admin.NET/Admin.NET.Core/Entity/SysOpenAccess.cs

@@ -51,8 +51,6 @@ public partial class SysOpenAccess : EntityBase
     /// <summary>
     /// 绑定用户
     /// </summary>
-    [Newtonsoft.Json.JsonIgnore]
-    [System.Text.Json.Serialization.JsonIgnore]
     [Navigate(NavigateType.OneToOne, nameof(BindUserId))]
     public SysUser BindUser { get; set; }
 }

+ 1 - 1
Admin.NET/Admin.NET.Core/Entity/SysOrg.cs

@@ -47,7 +47,7 @@ public partial class SysOrg : EntityTenant
     /// </summary>
     [SugarColumn(ColumnDescription = "机构类型", Length = 64)]
     [MaxLength(64)]
-    public string? Type { get; set; }
+    public virtual string? Type { get; set; }
 
     /// <summary>
     /// 负责人Id

+ 1 - 1
Admin.NET/Admin.NET.Core/Entity/SysPrint.cs

@@ -45,7 +45,7 @@ public partial class SysPrint : EntityTenant
     /// <summary>
     /// 打印参数
     /// </summary>
-    [SugarColumn(ColumnDescription = "打印参数", ColumnDataType = "text")]
+    [SugarColumn(ColumnDescription = "打印参数", ColumnDataType = StaticConfig.CodeFirst_BigString)]
     public virtual string? PrintParam { get; set; }
 
     /// <summary>

+ 190 - 164
Admin.NET/Admin.NET.Core/Entity/SysWechatPay.cs

@@ -1,165 +1,191 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Core;
-
-/// <summary>
-/// 系统微信支付表
-/// </summary>
-[SugarTable(null, "系统微信支付表")]
-[SysTable]
-public partial class SysWechatPay : EntityBase
-{
-    /// <summary>
-    /// 微信商户号
-    /// </summary>
-    [SugarColumn(ColumnDescription = "微信商户号")]
-    [Required]
-    public virtual string MerchantId { get; set; }
-
-    /// <summary>
-    /// 服务商AppId
-    /// </summary>
-    [SugarColumn(ColumnDescription = "服务商AppId")]
-    [Required]
-    public virtual string AppId { get; set; }
-
-    /// <summary>
-    /// 商户订单号
-    /// </summary>
-    [SugarColumn(ColumnDescription = "商户订单号")]
-    [Required]
-    public virtual string OutTradeNumber { get; set; }
-
-    /// <summary>
-    /// 支付订单号
-    /// </summary>
-    [SugarColumn(ColumnDescription = "支付订单号")]
-    [Required]
-    public virtual string TransactionId { get; set; }
-
-    /// <summary>
-    /// 交易类型
-    /// </summary>
-    [SugarColumn(ColumnDescription = "交易类型")]
-    public string? TradeType { get; set; }
-
-    /// <summary>
-    /// 交易状态
-    /// </summary>
-    [SugarColumn(ColumnDescription = "交易状态")]
-    public string? TradeState { get; set; }
-
-    /// <summary>
-    /// 交易状态描述
-    /// </summary>
-    [SugarColumn(ColumnDescription = "交易状态描述")]
-    public string? TradeStateDescription { get; set; }
-
-    /// <summary>
-    /// 付款银行类型
-    /// </summary>
-    [SugarColumn(ColumnDescription = "付款银行类型")]
-    public string? BankType { get; set; }
-
-    /// <summary>
-    /// 订单总金额
-    /// </summary>
-    [SugarColumn(ColumnDescription = "订单总金额")]
-    public int Total { get; set; }
-
-    /// <summary>
-    /// 用户支付金额
-    /// </summary>
-    [SugarColumn(ColumnDescription = "用户支付金额")]
-    public int? PayerTotal { get; set; }
-
-    /// <summary>
-    /// 支付完成时间
-    /// </summary>
-    [SugarColumn(ColumnDescription = "支付完成时间")]
-    public DateTimeOffset? SuccessTime { get; set; }
-
-    /// <summary>
-    /// 交易结束时间
-    /// </summary>
-    [SugarColumn(ColumnDescription = "交易结束时间")]
-    public DateTimeOffset? ExpireTime { get; set; }
-
-    /// <summary>
-    /// 商品描述
-    /// </summary>
-    [SugarColumn(ColumnDescription = "商品描述")]
-    public string? Description { get; set; }
-
-    /// <summary>
-    /// 场景信息
-    /// </summary>
-    [SugarColumn(ColumnDescription = "场景信息")]
-    public string? Scene { get; set; }
-
-    /// <summary>
-    /// 附加数据
-    /// </summary>
-    [SugarColumn(ColumnDescription = "附加数据")]
-    public string? Attachment { get; set; }
-
-    /// <summary>
-    /// 优惠标记
-    /// </summary>
-    [SugarColumn(ColumnDescription = "优惠标记")]
-    public string? GoodsTag { get; set; }
-
-    /// <summary>
-    /// 结算信息
-    /// </summary>
-    [SugarColumn(ColumnDescription = "结算信息")]
-    public string? Settlement { get; set; }
-
-    /// <summary>
-    /// 回调通知地址
-    /// </summary>
-    [SugarColumn(ColumnDescription = "回调通知地址")]
-    public string? NotifyUrl { get; set; }
-
-    /// <summary>
-    /// 备注
-    /// </summary>
-    [SugarColumn(ColumnDescription = "备注")]
-    public string? Remark { get; set; }
-
-    /// <summary>
-    /// 微信OpenId标识
-    /// </summary>
-    [SugarColumn(ColumnDescription = "微信OpenId标识")]
-    public string? OpenId { get; set; }
-
-    /// <summary>
-    /// 关联微信用户
-    /// </summary>
-    [Newtonsoft.Json.JsonIgnore]
-    [System.Text.Json.Serialization.JsonIgnore]
-    [Navigate(NavigateType.OneToOne, nameof(OpenId))]
-    public SysWechatUser SysWechatUser { get; set; }
-
-    /// <summary>
-    /// 子商户号
-    /// </summary>
-    [SugarColumn(ColumnDescription = "子商户号")]
-    public string? SubMerchantId { get; set; }
-
-    /// <summary>
-    /// 子商户AppId
-    /// </summary>
-    [SugarColumn(ColumnDescription = "回调通知地址")]
-    public string? SubAppId { get; set; }
-
-    /// <summary>
-    /// 子商户唯一标识
-    /// </summary>
-    [SugarColumn(ColumnDescription = "子商户唯一标识")]
-    public string? SubOpenId { get; set; }
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 系统微信支付表
+/// </summary>
+[SugarTable(null, "系统微信支付表")]
+[SysTable]
+[SugarIndex("idx_{table}_BusinessId", nameof(BusinessId), OrderByType.Asc)]
+[SugarIndex("idx_{table}_TradeState", nameof(TradeState), OrderByType.Asc)]
+[SugarIndex("idx_{table}_Tags", nameof(Tags), OrderByType.Asc)]
+public partial class SysWechatPay : EntityBase
+{
+    /// <summary>
+    /// 微信商户号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "微信商户号")]
+    [Required]
+    public virtual string MerchantId { get; set; }
+
+    /// <summary>
+    /// 服务商AppId
+    /// </summary>
+    [SugarColumn(ColumnDescription = "服务商AppId")]
+    [Required]
+    public virtual string AppId { get; set; }
+
+    /// <summary>
+    /// 商户订单号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "商户订单号")]
+    [Required]
+    public virtual string OutTradeNumber { get; set; }
+
+    /// <summary>
+    /// 支付订单号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "支付订单号")]
+    [Required]
+    public virtual string TransactionId { get; set; }
+
+    /// <summary>
+    /// 交易类型
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易类型")]
+    public string? TradeType { get; set; }
+
+    /// <summary>
+    /// 交易状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易状态")]
+    public string? TradeState { get; set; }
+
+    /// <summary>
+    /// 交易状态描述
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易状态描述")]
+    public string? TradeStateDescription { get; set; }
+
+    /// <summary>
+    /// 付款银行类型
+    /// </summary>
+    [SugarColumn(ColumnDescription = "付款银行类型")]
+    public string? BankType { get; set; }
+
+    /// <summary>
+    /// 订单总金额
+    /// </summary>
+    [SugarColumn(ColumnDescription = "订单总金额")]
+    public int Total { get; set; }
+
+    /// <summary>
+    /// 用户支付金额
+    /// </summary>
+    [SugarColumn(ColumnDescription = "用户支付金额")]
+    public int? PayerTotal { get; set; }
+
+    /// <summary>
+    /// 支付完成时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "支付完成时间")]
+    public DateTime? SuccessTime { get; set; }
+
+    /// <summary>
+    /// 交易结束时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易结束时间")]
+    public DateTime? ExpireTime { get; set; }
+
+    /// <summary>
+    /// 商品描述
+    /// </summary>
+    [SugarColumn(ColumnDescription = "商品描述")]
+    public string? Description { get; set; }
+
+    /// <summary>
+    /// 场景信息
+    /// </summary>
+    [SugarColumn(ColumnDescription = "场景信息")]
+    public string? Scene { get; set; }
+
+    /// <summary>
+    /// 附加数据
+    /// </summary>
+    [SugarColumn(ColumnDescription = "附加数据")]
+    public string? Attachment { get; set; }
+
+    /// <summary>
+    /// 优惠标记
+    /// </summary>
+    [SugarColumn(ColumnDescription = "优惠标记")]
+    public string? GoodsTag { get; set; }
+
+    /// <summary>
+    /// 结算信息
+    /// </summary>
+    [SugarColumn(ColumnDescription = "结算信息")]
+    public string? Settlement { get; set; }
+
+    /// <summary>
+    /// 回调通知地址
+    /// </summary>
+    [SugarColumn(ColumnDescription = "回调通知地址")]
+    public string? NotifyUrl { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    [SugarColumn(ColumnDescription = "备注")]
+    public string? Remark { get; set; }
+
+    /// <summary>
+    /// 微信OpenId标识
+    /// </summary>
+    [SugarColumn(ColumnDescription = "微信OpenId标识")]
+    public string? OpenId { get; set; }
+
+    /// <summary>
+    /// 业务标签,用来区分做什么业务
+    /// </summary>
+    /// <remarks>
+    /// Tags标识用来区分这个支付记录对应什么业务从而确定相关联的表名,
+    /// 再结合BusinessId保存了对应的业务数据的ID,就可以确定这个支付
+    /// 记录与哪一条业务数据相关联
+    /// </remarks>
+    [SugarColumn(ColumnDescription = "业务标签,用来区分做什么业务", Length = 64)]
+    public string? Tags { get; set; }
+
+    /// <summary>
+    /// 对应业务的主键
+    /// </summary>
+    [SugarColumn(ColumnDescription = "对应业务的主键")]
+    public long BusinessId { get; set; }
+
+    /// <summary>
+    /// 付款二维码内容
+    /// </summary>
+    [SugarColumn(ColumnDescription = "付款二维码内容")]
+    public string? QrcodeContent { get; set; }
+
+    /// <summary>
+    /// 关联微信用户
+    /// </summary>
+    [Newtonsoft.Json.JsonIgnore]
+    [System.Text.Json.Serialization.JsonIgnore]
+    [Navigate(NavigateType.OneToOne, nameof(OpenId))]
+    public SysWechatUser SysWechatUser { get; set; }
+
+    /// <summary>
+    /// 子商户号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "子商户号")]
+    public string? SubMerchantId { get; set; }
+
+    /// <summary>
+    /// 子商户AppId
+    /// </summary>
+    [SugarColumn(ColumnDescription = "回调通知地址")]
+    public string? SubAppId { get; set; }
+
+    /// <summary>
+    /// 子商户唯一标识
+    /// </summary>
+    [SugarColumn(ColumnDescription = "子商户唯一标识")]
+    public string? SubOpenId { get; set; }
 }

+ 97 - 0
Admin.NET/Admin.NET.Core/Entity/SysWechatRefund.cs

@@ -0,0 +1,97 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 系统微信支付退款表
+/// </summary>
+[SugarTable(null, "系统微信支付退款表")]
+[SysTable]
+[SugarIndex("idx_{table}_WechatPayId", nameof(WechatPayId), OrderByType.Asc)]
+public partial class SysWechatRefund : EntityBase
+{
+    /// <summary>
+    /// 定单主键
+    /// </summary>
+    [SugarColumn(ColumnDescription = "定单主键")]
+    public long WechatPayId { get; set; }
+
+    /// <summary>
+    /// 商户退款号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "商户退款号")]
+    [Required]
+    public virtual string OutRefundNumber { get; set; }
+
+    /// <summary>
+    /// 退款订单号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "退款订单号")]
+    [Required]
+    public virtual string TransactionId { get; set; }
+
+    /// <summary>
+    /// 退款原因
+    /// </summary>
+    [SugarColumn(ColumnDescription = "退款原因")]
+    public string? Reason { get; set; }
+
+    /// <summary>
+    /// 退款渠道
+    /// </summary>
+    [SugarColumn(ColumnDescription = "退款渠道")]
+    public string? Channel { get; set; }
+
+    /// <summary>
+    /// 退款入账账户
+    /// </summary>
+    /// <remarks>
+    /// 取当前退款单的退款入账方,有以下几种情况:
+    /// 1)退回银行卡:{银行名称}{卡类型}{ 卡尾号}
+    /// 2)退回支付用户零钱: 支付用户零钱
+    /// 3)退还商户: 商户基本账户商户结算银行账户
+    /// 4)退回支付用户零钱通: 支付用户零钱通
+    /// </remarks>
+    [SugarColumn(ColumnDescription = "退款入账账户")]
+    public string? UserReceivedAccount { get; set; }
+
+    /// <summary>
+    /// 退款状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "退款状态")]
+    public string? TradeState { get; set; }
+
+    /// <summary>
+    /// 交易状态描述
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易状态描述")]
+    public string? TradeStateDescription { get; set; }
+
+    /// <summary>
+    /// 订单总金额
+    /// </summary>
+    [SugarColumn(ColumnDescription = "退款金额")]
+    public int Refund { get; set; }
+
+    /// <summary>
+    /// 支完成时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "完成时间")]
+    public DateTime? SuccessTime { get; set; }
+
+    /// <summary>
+    /// 回调通知地址
+    /// </summary>
+    [SugarColumn(ColumnDescription = "回调通知地址")]
+    public string? NotifyUrl { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    [SugarColumn(ColumnDescription = "备注")]
+    public string? Remark { get; set; }
+}

+ 9 - 3
Admin.NET/Admin.NET.Core/Enum/ErrorCodeEnum.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -205,6 +205,12 @@ public enum ErrorCodeEnum
     [ErrorCodeItemMetadata("新密码不能与旧密码相同")]
     D1028,
 
+    /// <summary>
+    /// 系统默认账号禁止删除
+    /// </summary>
+    [ErrorCodeItemMetadata("系统默认账号禁止删除")]
+    D1029,
+
     /// <summary>
     /// 父机构不存在
     /// </summary>
@@ -254,9 +260,9 @@ public enum ErrorCodeEnum
     D2007,
 
     /// <summary>
-    /// 租户默认机构禁止删除
+    /// 系统默认机构禁止删除
     /// </summary>
-    [ErrorCodeItemMetadata("租户默认机构禁止删除")]
+    [ErrorCodeItemMetadata("系统默认机构禁止删除")]
     D2008,
 
     /// <summary>

+ 9 - 11
Admin.NET/Admin.NET.Core/EventBus/EventConsumer.cs

@@ -12,18 +12,20 @@ namespace Admin.NET.Core;
 /// <typeparam name="T"></typeparam>
 public class EventConsumer<T> : IDisposable
 {
+    /// <summary>
+    ///
+    /// </summary>
     private Task _consumerTask;
-    private CancellationTokenSource _consumerCts;
 
     /// <summary>
-    /// 消费者
+    ///
     /// </summary>
-    public IProducerConsumer<T> Consumer { get; }
+    private CancellationTokenSource _consumerCts;
 
     /// <summary>
-    /// ConsumerBuilder
+    /// 消费者
     /// </summary>
-    public FullRedis Builder { get; set; }
+    public IProducerConsumer<T> Consumer { get; }
 
     /// <summary>
     /// 消息回调
@@ -33,11 +35,8 @@ public class EventConsumer<T> : IDisposable
     /// <summary>
     /// 构造函数
     /// </summary>
-    public EventConsumer(FullRedis redis, string routeKey)
-    {
-        Builder = redis;
-        Consumer = Builder.GetQueue<T>(routeKey);
-    }
+    /// <param name="consumer"></param>
+    public EventConsumer(IProducerConsumer<T> consumer) => Consumer = consumer;
 
     /// <summary>
     /// 启动
@@ -106,7 +105,6 @@ public class EventConsumer<T> : IDisposable
             {
                 Stop().Wait();
             }
-            Builder.Dispose();
         }
     }
 }

+ 31 - 0
Admin.NET/Admin.NET.Core/EventBus/EventHandlerMonitor.cs

@@ -0,0 +1,31 @@
+// 麻省理工学院许可证
+//
+// 版权所有 (c) 2021-2023 zuohuaijun,大名科技(天津)有限公司  联系电话/微信:18020030720  QQ:515096995
+//
+// 特此免费授予获得本软件的任何人以处理本软件的权利,但须遵守以下条件:在所有副本或重要部分的软件中必须包括上述版权声明和本许可声明。
+//
+// 软件按“原样”提供,不提供任何形式的明示或暗示的保证,包括但不限于对适销性、适用性和非侵权的保证。
+// 在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是因合同、侵权或其他方式引起的,与软件或其使用或其他交易有关。
+
+namespace Admin.NET.Core;
+
+public class EventHandlerMonitor : IEventHandlerMonitor
+{
+    public Task OnExecutingAsync(EventHandlerExecutingContext context)
+    {
+        //_logger.LogInformation("执行之前:{EventId}", context.Source.EventId);
+        return Task.CompletedTask;
+    }
+
+    public Task OnExecutedAsync(EventHandlerExecutedContext context)
+    {
+        //_logger.LogInformation("执行之后:{EventId}", context.Source.EventId);
+
+        if (context.Exception != null)
+        {
+            Log.Error($"EventHandlerMonitor.OnExecutedAsync 执行出错啦:{context.Source.EventId}", context.Exception);
+        }
+
+        return Task.CompletedTask;
+    }
+}

+ 15 - 15
Admin.NET/Admin.NET.Core/EventBus/RedisEventSourceStorer.cs

@@ -23,10 +23,11 @@ public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
     /// </summary>
     private readonly Channel<IEventSource> _channel;
 
-    /// <summary>
-    /// Redis 连接对象
-    /// </summary>
-    private readonly FullRedis _redis;
+    ///// <summary>
+    ///// Redis 连接对象
+    ///// </summary>
+    //private readonly FullRedis _redis;
+    private IProducerConsumer<ChannelEventSource> _queue;
 
     /// <summary>
     /// 路由键
@@ -36,10 +37,10 @@ public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
     /// <summary>
     /// 构造函数
     /// </summary>
-    /// <param name="redis">Redis 连接对象</param>
+    /// <param name="cacheProvider">Redis 连接对象</param>
     /// <param name="routeKey">路由键</param>
     /// <param name="capacity">存储器最多能够处理多少消息,超过该容量进入等待写入</param>
-    public RedisEventSourceStorer(ICache redis, string routeKey, int capacity)
+    public RedisEventSourceStorer(ICacheProvider cacheProvider, string routeKey, int capacity)
     {
         // 配置通道,设置超出默认容量后进入等待
         var boundedChannelOptions = new BoundedChannelOptions(capacity)
@@ -50,11 +51,12 @@ public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
         // 创建有限容量通道
         _channel = Channel.CreateBounded<IEventSource>(boundedChannelOptions);
 
-        _redis = redis as FullRedis;
+        //_redis = redis as FullRedis;
         _routeKey = routeKey;
 
         // 创建消息订阅者
-        _eventConsumer = new EventConsumer<ChannelEventSource>(_redis, _routeKey);
+        _queue = cacheProvider.GetQueue<ChannelEventSource>(routeKey);
+        _eventConsumer = new EventConsumer<ChannelEventSource>(_queue);
 
         // 订阅消息写入 Channel
         _eventConsumer.Received += (send, cr) =>
@@ -63,10 +65,7 @@ public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
             //var eventSource = JsonConvert.DeserializeObject<ChannelEventSource>(cr);
 
             // 写入内存管道存储器
-            Task.Run(async () =>
-            {
-                await _channel.Writer.WriteAsync(cr);
-            });
+            _channel.Writer.WriteAsync(cr);
         };
 
         // 启动消费者
@@ -93,13 +92,14 @@ public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
             // 序列化消息
             //var data = JsonSerializer.Serialize(source);
 
-            // 获取一个订阅对象
-            var queue = _redis.GetQueue<ChannelEventSource>(_routeKey);
+            //// 获取一个订阅对象
+            //var queue = _redis.GetQueue<ChannelEventSource>(_routeKey);
 
             // 异步发布
             await Task.Factory.StartNew(() =>
             {
-                queue.Add(source);
+                //queue.Add(source);
+                _queue.Add(source);
             }, cancellationToken, TaskCreationOptions.LongRunning, System.Threading.Tasks.TaskScheduler.Default);
         }
         else

+ 48 - 107
Admin.NET/Admin.NET.Core/EventBus/RedisQueue.cs

@@ -13,72 +13,26 @@ namespace Admin.NET.Core;
 /// </summary>
 public static class RedisQueue
 {
-    private static readonly ICache _cache = App.GetRequiredService<ICache>();
+    private static ICacheProvider _cacheProvider = App.GetRequiredService<ICacheProvider>();
 
-    /// <summary>
-    /// 获取普通队列
-    /// </summary>
+    /// <summary>创建Redis消息队列。默认消费一次,指定消费者group时使用STREAM结构,支持多消费组共享消息</summary>
+    /// <remarks>
+    /// 使用队列时,可根据是否设置消费组来决定使用简单队列还是完整队列。 简单队列(如RedisQueue)可用作命令队列,Topic很多,但几乎没有消息。 完整队列(如RedisStream)可用作消息队列,Topic很少,但消息很多,并且支持多消费组。
+    /// </remarks>
     /// <typeparam name="T"></typeparam>
-    /// <param name="topic"></param>
+    /// <param name="topic">主题</param>
+    /// <param name="group">消费组。未指定消费组时使用简单队列(如RedisQueue),指定消费组时使用完整队列(如RedisStream)</param>
     /// <returns></returns>
-    public static IProducerConsumer<T> GetQueue<T>(string topic)
+    public static IProducerConsumer<T> GetQueue<T>(String topic, String group = null)
     {
-        var queue = (_cache as FullRedis).GetQueue<T>(topic);
-        return queue;
-    }
+        // 队列需要单列
+        var key = $"myStream:{topic}";
+        if (_cacheProvider.InnerCache.TryGetValue<IProducerConsumer<T>>(key, out var queue)) return queue;
 
-    /// <summary>
-    /// 发送一个数据到队列
-    /// </summary>
-    /// <typeparam name="T"></typeparam>
-    /// <param name="topic"></param>
-    /// <param name="value"></param>
-    /// <returns></returns>
-    public static int AddQueue<T>(string topic, T value)
-    {
-        var queue = GetQueue<T>(topic);
-        return queue.Add(value);
-    }
+        queue = _cacheProvider.GetQueue<T>(topic, group);
+        _cacheProvider.Cache.Set(key, queue);
 
-    /// <summary>
-    /// 发送一个数据列表到队列
-    /// </summary>
-    /// <param name="topic"></param>
-    /// <param name="value"></param>
-    /// <typeparam name="T"></typeparam>
-    /// <returns></returns>
-    public static int AddQueueList<T>(string topic, List<T> value)
-    {
-        var queue = GetQueue<T>(topic);
-        var count = queue.Count;
-        var result = queue.Add(value.ToArray());
-        return result - count;
-    }
-
-    /// <summary>
-    /// 获取一批队列消息
-    /// </summary>
-    /// <typeparam name="T"></typeparam>
-    /// <param name="topic"></param>
-    /// <param name="count"></param>
-    /// <returns></returns>
-    public static List<T> Take<T>(string topic, int count = 1)
-    {
-        var queue = GetQueue<T>(topic);
-        var result = queue.Take(count).ToList();
-        return result;
-    }
-
-    /// <summary>
-    /// 获取一个队列消息
-    /// </summary>
-    /// <typeparam name="T"></typeparam>
-    /// <param name="topic"></param>
-    /// <returns></returns>
-    public static async Task<T> TakeOneAsync<T>(string topic)
-    {
-        var queue = GetQueue<T>(topic);
-        return await queue.TakeOneAsync(1);
+        return queue;
     }
 
     /// <summary>
@@ -89,7 +43,13 @@ public static class RedisQueue
     /// <returns></returns>
     public static RedisReliableQueue<T> GetRedisReliableQueue<T>(string topic)
     {
-        var queue = (_cache as FullRedis).GetReliableQueue<T>(topic);
+        // 队列需要单列
+        var key = $"myQueue:{topic}";
+        if (_cacheProvider.InnerCache.TryGetValue<RedisReliableQueue<T>>(key, out var queue)) return queue;
+
+        queue = (_cacheProvider.Cache as FullRedis).GetReliableQueue<T>(topic);
+        _cacheProvider.Cache.Set(key, queue);
+
         return queue;
     }
 
@@ -115,7 +75,7 @@ public static class RedisQueue
     /// <returns></returns>
     public static int AddReliableQueueList<T>(string topic, List<T> value)
     {
-        var queue = (_cache as FullRedis).GetReliableQueue<T>(topic);
+        var queue = GetRedisReliableQueue<T>(topic);
         var count = queue.Count;
         var result = queue.Add(value.ToArray());
         return result - count;
@@ -130,49 +90,12 @@ public static class RedisQueue
     /// <returns></returns>
     public static int AddReliableQueue<T>(string topic, T value)
     {
-        var queue = (_cache as FullRedis).GetReliableQueue<T>(topic);
+        var queue = GetRedisReliableQueue<T>(topic);
         var count = queue.Count;
         var result = queue.Add(value);
         return result - count;
     }
 
-    /// <summary>
-    /// 在可信队列获取一条数据
-    /// </summary>
-    /// <param name="topic"></param>
-    /// <typeparam name="T"></typeparam>
-    /// <returns></returns>
-    public static T ReliableTakeOne<T>(string topic)
-    {
-        var queue = GetRedisReliableQueue<T>(topic);
-        return queue.TakeOne(1);
-    }
-
-    /// <summary>
-    /// 异步在可信队列获取一条数据
-    /// </summary>
-    /// <param name="topic"></param>
-    /// <typeparam name="T"></typeparam>
-    /// <returns></returns>
-    public static async Task<T> ReliableTakeOneAsync<T>(string topic)
-    {
-        var queue = GetRedisReliableQueue<T>(topic);
-        return await queue.TakeOneAsync(1);
-    }
-
-    /// <summary>
-    /// 在可信队列获取多条数据
-    /// </summary>
-    /// <param name="topic"></param>
-    /// <param name="count"></param>
-    /// <typeparam name="T"></typeparam>
-    /// <returns></returns>
-    public static List<T> ReliableTake<T>(string topic, int count)
-    {
-        var queue = GetRedisReliableQueue<T>(topic);
-        return queue.Take(count).ToList();
-    }
-
     /// <summary>
     /// 获取延迟队列
     /// </summary>
@@ -181,7 +104,13 @@ public static class RedisQueue
     /// <returns></returns>
     public static RedisDelayQueue<T> GetDelayQueue<T>(string topic)
     {
-        var queue = (_cache as FullRedis).GetDelayQueue<T>(topic);
+        // 队列需要单列
+        var key = $"myDelay:{topic}";
+        if (_cacheProvider.InnerCache.TryGetValue<RedisDelayQueue<T>>(key, out var queue)) return queue;
+
+        queue = (_cacheProvider.Cache as FullRedis).GetDelayQueue<T>(topic);
+        _cacheProvider.Cache.Set(key, queue);
+
         return queue;
     }
 
@@ -215,27 +144,39 @@ public static class RedisQueue
     }
 
     /// <summary>
-    /// 异步在延迟队列获取一条数据
+    /// 在可信队列获取一条数据
     /// </summary>
     /// <param name="topic"></param>
     /// <typeparam name="T"></typeparam>
     /// <returns></returns>
-    public static async Task<T> DelayTakeOne<T>(string topic)
+    public static T ReliableTakeOne<T>(string topic)
     {
-        var queue = GetDelayQueue<T>(topic);
+        var queue = GetRedisReliableQueue<T>(topic);
+        return queue.TakeOne(1);
+    }
+
+    /// <summary>
+    /// 异步在可信队列获取一条数据
+    /// </summary>
+    /// <param name="topic"></param>
+    /// <typeparam name="T"></typeparam>
+    /// <returns></returns>
+    public static async Task<T> ReliableTakeOneAsync<T>(string topic)
+    {
+        var queue = GetRedisReliableQueue<T>(topic);
         return await queue.TakeOneAsync(1);
     }
 
     /// <summary>
-    /// 在延迟队列获取多条数据
+    /// 在可信队列获取多条数据
     /// </summary>
     /// <param name="topic"></param>
     /// <param name="count"></param>
     /// <typeparam name="T"></typeparam>
     /// <returns></returns>
-    public static List<T> DelayTake<T>(string topic, int count = 1)
+    public static List<T> ReliableTake<T>(string topic, int count)
     {
-        var queue = GetDelayQueue<T>(topic);
+        var queue = GetRedisReliableQueue<T>(topic);
         return queue.Take(count).ToList();
     }
 }

+ 43 - 21
Admin.NET/Admin.NET.Core/EventBus/RetryEventHandlerExecutor.cs

@@ -1,22 +1,44 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Core;
-
-/// <summary>
-/// 事件执行器-超时控制、失败重试熔断等等
-/// </summary>
-public class RetryEventHandlerExecutor : IEventHandlerExecutor
-{
-    public async Task ExecuteAsync(EventHandlerExecutingContext context, Func<EventHandlerExecutingContext, Task> handler)
-    {
-        // 如果执行失败,每隔 1s 重试,最多三次
-        await Retry.InvokeAsync(async () =>
-        {
-            await handler(context);
-        }, 3, 1000);
-    }
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 事件执行器-超时控制、失败重试熔断等等
+/// </summary>
+public class RetryEventHandlerExecutor : IEventHandlerExecutor
+{
+    public async Task ExecuteAsync(EventHandlerExecutingContext context, Func<EventHandlerExecutingContext, Task> handler)
+    {
+        var eventSubscribeAttribute = context.Attribute;
+        // 判断是否自定义了重试失败回调服务
+        var fallbackPolicyService = eventSubscribeAttribute?.FallbackPolicy == null
+            ? null
+            : App.GetService(eventSubscribeAttribute.FallbackPolicy) as IEventFallbackPolicy;
+
+        await Retry.InvokeAsync(async () =>
+        {
+            try
+            {
+                await handler(context);
+            }
+            catch (Exception ex)
+            {
+                Log.Error($"Invoke EventHandler {context.Source.EventId} Error", ex);
+                throw;
+            }
+        }
+        , eventSubscribeAttribute?.NumRetries ?? 0
+        , eventSubscribeAttribute?.RetryTimeout ?? 1000
+        , exceptionTypes: eventSubscribeAttribute?.ExceptionTypes
+        , fallbackPolicy: fallbackPolicyService == null ? null : async (Exception ex) => { await fallbackPolicyService.CallbackAsync(context, ex); }
+        , retryAction: (total, times) =>
+        {
+            // 输出重试日志
+            Log.Warning($"Retrying {times}/{total} times for  EventHandler {context.Source.EventId}");
+        });
+    }
 }

+ 18 - 1
Admin.NET/Admin.NET.Core/Extension/EnumExtension.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -166,6 +166,17 @@ public static class EnumExtension
             ?.GetCustomAttribute<DescriptionAttribute>()?.Description;
     }
 
+    /// <summary>
+    /// 获取枚举的Theme
+    /// </summary>
+    /// <param name="value"></param>
+    /// <returns></returns>
+    public static string GetTheme(this object value)
+    {
+        return value.GetType().GetMember(value.ToString() ?? string.Empty).FirstOrDefault()
+            ?.GetCustomAttribute<ThemeAttribute>()?.Theme;
+    }
+
     /// <summary>
     /// 将枚举转成枚举信息集合
     /// </summary>
@@ -183,6 +194,7 @@ public static class EnumExtension
             {
                 Name = item.ToString(),
                 Describe = item.GetDescription() ?? item.ToString(),
+                Theme = item.GetTheme() ?? string.Empty,
                 Value = item.GetHashCode()
             };
         }).ToList();
@@ -213,6 +225,11 @@ public class EnumEntity
     /// </summary>
     public string Describe { set; get; }
 
+    /// <summary>
+    /// 枚举的样式
+    /// </summary>
+    public string Theme { set; get; }
+
     /// <summary>
     /// 枚举名称
     /// </summary>

+ 2 - 1
Admin.NET/Admin.NET.Core/Job/EnumToDictJob.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -59,6 +59,7 @@ public class EnumToDictJob : IJob
                                 dictData.Value = enumData.Value.ToString();
                                 dictData.Code = enumData.Name;
                                 dictData.OrderNo = enumData.Value + 10;
+                                dictData.Name = enumData.Describe;
                                 uSysDictData.Add(dictData);
                             }
                         });

+ 3 - 2
Admin.NET/Admin.NET.Core/Job/LogJob.cs

@@ -29,15 +29,16 @@ public class LogJob : IJob
         var logVisRep = serviceScope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysLogVis>>();
         var logOpRep = serviceScope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysLogOp>>();
         var logDiffRep = serviceScope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysLogDiff>>();
+        var sysConfigService = serviceScope.ServiceProvider.GetRequiredService<SysConfigService>();
 
-        var daysAgo = 30; // 删除30天以前
+        var daysAgo = await sysConfigService.GetConfigValue<int>(CommonConst.SysLogRetentionDays); // 日志保留天数
         await logVisRep.CopyNew().AsDeleteable().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken); // 删除访问日志
         await logOpRep.CopyNew().AsDeleteable().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken); // 删除操作日志
         await logDiffRep.CopyNew().AsDeleteable().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken); // 删除差异日志
 
         var originColor = Console.ForegroundColor;
         Console.ForegroundColor = ConsoleColor.Yellow;
-        Console.WriteLine($"【{DateTime.Now}】清理系统日志(30天前)");
+        Console.WriteLine($"【{DateTime.Now}】清理系统日志({daysAgo}天前)");
         Console.ForegroundColor = originColor;
 
         // 自定义日志

+ 12 - 28
Admin.NET/Admin.NET.Core/Logging/DatabaseLoggingWriter.cs

@@ -4,8 +4,6 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
-using IPTools.Core;
-
 namespace Admin.NET.Core;
 
 /// <summary>
@@ -52,8 +50,8 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable
         }
 
         var loggingMonitor = JSON.Deserialize<dynamic>(jsonStr);
-        // 记录数据校验日志
-        if (loggingMonitor.validation != null) return;
+        // 记录数据校验日志
+        if (loggingMonitor.validation != null && !await _sysConfigService.GetConfigValue<bool>(CommonConst.SysValidationLog)) return;
 
         // 获取当前操作者
         string account = "", realName = "", userId = "", tenantId = "";
@@ -72,8 +70,14 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable
             }
         }
 
-        string remoteIPv4 = loggingMonitor.remoteIPv4;
-        (string ipLocation, double? longitude, double? latitude) = GetIpAddress(remoteIPv4);
+        // 优先获取 X-Forwarded-For 头部信息携带的IP地址(如nginx代理配置转发)
+        var remoteIPv4 = ((JArray)loggingMonitor.requestHeaders).OfType<JObject>()
+            .FirstOrDefault(header => (string)header["key"] == "X-Forwarded-For")?["value"]?.ToString();
+
+        if (string.IsNullOrEmpty(remoteIPv4))
+            remoteIPv4 = loggingMonitor.remoteIPv4;
+
+        (string ipLocation, double? longitude, double? latitude) = CommonUtil.GetIpAddress(remoteIPv4);
 
         var browser = "";
         var os = "";
@@ -123,7 +127,7 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable
                 // 将异常日志发送到邮件
                 if (await _sysConfigService.GetConfigValue<bool>(CommonConst.SysErrorMail))
                 {
-                    await App.GetRequiredService<IEventPublisher>().PublishAsync(CommonConst.SendErrorMail, loggingMonitor.exception);
+                    await App.GetRequiredService<IEventPublisher>().PublishAsync(CommonConst.SendErrorMail, logMsg.Exception ?? loggingMonitor.exception);
                 }
 
                 return;
@@ -156,7 +160,7 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable
             }
 
             // 记录操作日志
-            if (!(await _sysConfigService.GetConfigValue<bool>(CommonConst.SysOpLog))) return;
+            if (!await _sysConfigService.GetConfigValue<bool>(CommonConst.SysOpLog)) return;
             await _db.Insertable(new SysLogOp
             {
                 ControllerName = loggingMonitor.controllerName,
@@ -195,26 +199,6 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable
         }
     }
 
-    /// <summary>
-    /// 解析IP地址
-    /// </summary>
-    /// <param name="ip"></param>
-    /// <returns></returns>
-    internal static (string ipLocation, double? longitude, double? latitude) GetIpAddress(string ip)
-    {
-        try
-        {
-            var ipInfo = IpTool.SearchWithI18N(ip); // 国际化查询,默认中文 中文zh-CN、英文en
-            var addressList = new List<string>() { ipInfo.Country, ipInfo.Province, ipInfo.City, ipInfo.NetworkOperator };
-            return (string.Join(" ", addressList.Where(u => u != "0" && !string.IsNullOrWhiteSpace(u)).ToList()), ipInfo.Longitude, ipInfo.Latitude); // 去掉0及空并用空格连接
-        }
-        catch
-        {
-            // 不做处理
-        }
-        return ("未知", 0, 0);
-    }
-
     /// <summary>
     /// 释放服务作用域
     /// </summary>

+ 2 - 2
Admin.NET/Admin.NET.Core/Logging/ElasticSearchLoggingWriter.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -57,7 +57,7 @@ public class ElasticSearchLoggingWriter : IDatabaseLoggingWriter, IDisposable
         }
 
         string remoteIPv4 = loggingMonitor.remoteIPv4;
-        (string ipLocation, double? longitude, double? latitude) = DatabaseLoggingWriter.GetIpAddress(remoteIPv4);
+        (string ipLocation, double? longitude, double? latitude) = CommonUtil.GetIpAddress(remoteIPv4);
 
         var sysLogOp = new SysLogOp
         {

+ 18 - 3
Admin.NET/Admin.NET.Core/Option/SMSOptions.cs

@@ -40,12 +40,27 @@ public sealed class SMSSettings
     public string AccessKeySecret { get; set; }
 
     /// <summary>
-    /// 短信签名
+    /// Templates
     /// </summary>
-    public string SignName { get; set; }
+    public List<SmsTemplate> Templates { get; set; }
 
     /// <summary>
-    /// 短信模板
+    /// GetTemplate
     /// </summary>
+    public SmsTemplate GetTemplate(string id = "0")
+    {
+        foreach (var template in Templates)
+        {
+            if (template.Id == id) { return template; }
+        }
+        return null;
+    }
+}
+
+public class SmsTemplate
+{
+    public string Id { get; set; } = string.Empty;
+    public string SignName { get; set; }
     public string TemplateCode { get; set; }
+    public string Content { get; set; }
 }

+ 19 - 16
Admin.NET/Admin.NET.Core/SeedData/SysConfigSeedData.cs

@@ -21,22 +21,25 @@ public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
         {
             new SysConfig{ Id=1300000000101, Name="演示环境", Code="sys_demo", Value="False", SysFlag=YesNoEnum.Y, Remark="演示环境", OrderNo=10, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000111, Name="默认密码", Code="sys_password", Value="123456", SysFlag=YesNoEnum.Y, Remark="默认密码", OrderNo=20, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000121, Name="记录操作日志", Code="sys_oplog", Value="True", SysFlag=YesNoEnum.Y, Remark="是否记录操作日志", OrderNo=30, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000131, Name="开启单设备登录", Code="sys_single_login", Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启单设备登录", OrderNo=40, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000141, Name="开启登录二次验证", Code="sys_second_ver", Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启登录二次验证", OrderNo=50, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000151, Name="开启图形验证码", Code="sys_captcha", Value="True", SysFlag=YesNoEnum.Y, Remark="是否开启图形验证码", OrderNo=60, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000161, Name="Token过期时间", Code="sys_token_expire", Value="10080", SysFlag=YesNoEnum.Y, Remark="Token过期时间(分钟)", OrderNo=70, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000171, Name="刷新Token过期时间", Code="sys_refresh_token_expire", Value="20160", SysFlag=YesNoEnum.Y, Remark="刷新Token过期时间(分钟)(一般 refresh_token 的有效时间 > 2 * access_token 的有效时间)", OrderNo=80, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000181, Name="发送异常日志邮件", Code="sys_error_mail", Value="True", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=90, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000191, Name="开启域登录验证", Code="sys_domain_login", Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启域登录验证", OrderNo=100, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000211, Name="系统主标题", Code="sys_web_title", Value="Admin.NET", SysFlag=YesNoEnum.Y, Remark="系统主标题", OrderNo=110, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000221, Name="系统副标题", Code="sys_web_viceTitle", Value="Admin.NET", SysFlag=YesNoEnum.Y, Remark="系统副标题", OrderNo=120, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000231, Name="系统描述", Code="sys_web_viceDesc", Value="站在巨人肩膀上的 .NET 通用权限开发框架", SysFlag=YesNoEnum.Y, Remark="系统描述", OrderNo=130, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000241, Name="水印内容", Code="sys_web_watermark", Value="Admin.NET", SysFlag=YesNoEnum.Y, Remark="水印内容", OrderNo=140, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000251, Name="版权说明", Code="sys_web_copyright", Value="Copyright © 2021-present Admin.NET All rights reserved.", SysFlag=YesNoEnum.Y, Remark="版权说明", OrderNo=150, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000261, Name="系统图标", Code="sys_web_logo", Value="/Upload/logo.png", SysFlag=YesNoEnum.Y, Remark="系统图标", OrderNo=160, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000271, Name="ICP备案号", Code="sys_web_icp", Value="省ICP备12345678号", SysFlag=YesNoEnum.Y, Remark="ICP备案号", OrderNo=170, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000281, Name="ICP地址", Code="sys_web_icpUrl", Value="https://beian.miit.gov.cn", SysFlag=YesNoEnum.Y, Remark="ICP地址", OrderNo=180, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000121, Name="密码最大错误次数", Code="sys_password_max_error_times", Value="5", SysFlag=YesNoEnum.Y, Remark="允许密码最大输入错误次数", OrderNo=30, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000131, Name="日志保留天数", Code="sys_log_retention_days", Value="180", SysFlag=YesNoEnum.Y, Remark="日志保留天数(天)", OrderNo=40, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000141, Name="记录操作日志", Code="sys_oplog", Value="True", SysFlag=YesNoEnum.Y, Remark="是否记录操作日志", OrderNo=50, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000151, Name="开启单设备登录", Code="sys_single_login", Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启单设备登录", OrderNo=60, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000161, Name="开启登录二次验证", Code="sys_second_ver", Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启登录二次验证", OrderNo=70, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000171, Name="开启图形验证码", Code="sys_captcha", Value="True", SysFlag=YesNoEnum.Y, Remark="是否开启图形验证码", OrderNo=80, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000181, Name="Token过期时间", Code="sys_token_expire", Value="10080", SysFlag=YesNoEnum.Y, Remark="Token过期时间(分钟)", OrderNo=90, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000191, Name="刷新Token过期时间", Code="sys_refresh_token_expire", Value="20160", SysFlag=YesNoEnum.Y, Remark="刷新Token过期时间(分钟)(一般 refresh_token 的有效时间 > 2 * access_token 的有效时间)", OrderNo=100, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000201, Name="发送异常日志邮件", Code="sys_error_mail", Value="True", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=110, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000211, Name="开启域登录验证", Code="sys_domain_login", Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启域登录验证", OrderNo=120, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000221, Name="开启数据校验日志", Code="sys_validation_log", Value="True", SysFlag=YesNoEnum.Y, Remark="是否数据校验日志", OrderNo=130, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000301, Name="系统主标题", Code="sys_web_title", Value="Admin.NET", SysFlag=YesNoEnum.Y, Remark="系统主标题", OrderNo=300, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000311, Name="系统副标题", Code="sys_web_viceTitle", Value="Admin.NET", SysFlag=YesNoEnum.Y, Remark="系统副标题", OrderNo=310, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000321, Name="系统描述", Code="sys_web_viceDesc", Value="站在巨人肩膀上的 .NET 通用权限开发框架", SysFlag=YesNoEnum.Y, Remark="系统描述", OrderNo=320, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000331, Name="水印内容", Code="sys_web_watermark", Value="Admin.NET", SysFlag=YesNoEnum.Y, Remark="水印内容", OrderNo=330, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000341, Name="版权说明", Code="sys_web_copyright", Value="Copyright © 2021-present Admin.NET All rights reserved.", SysFlag=YesNoEnum.Y, Remark="版权说明", OrderNo=340, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000351, Name="系统图标", Code="sys_web_logo", Value="/Upload/logo.png", SysFlag=YesNoEnum.Y, Remark="系统图标", OrderNo=350, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000361, Name="ICP备案号", Code="sys_web_icp", Value="省ICP备12345678号", SysFlag=YesNoEnum.Y, Remark="ICP备案号", OrderNo=360, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000371, Name="ICP地址", Code="sys_web_icpUrl", Value="https://beian.miit.gov.cn", SysFlag=YesNoEnum.Y, Remark="ICP地址", OrderNo=370, GroupCode="WebConfig", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
         };
     }
 }

+ 205 - 195
Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs

@@ -1,196 +1,206 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Core;
-
-/// <summary>
-/// 系统菜单表种子数据
-/// </summary>
-public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
-{
-    /// <summary>
-    /// 种子数据
-    /// </summary>
-    /// <returns></returns>
-    public IEnumerable<SysMenu> HasData()
-    {
-        return new[]
-        {
-            new SysMenu{ Id=1300000000101, Pid=0, Title="工作台", Path="/dashboard", Name="dashboard", Component="Layout", Icon="ele-HomeFilled", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=0 },
-            new SysMenu{ Id=1300000000111, Pid=1300000000101, Title="工作台", Path="/dashboard/home", Name="home", Component="/home/index", IsAffix=true, Icon="ele-HomeFilled", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1300000000121, Pid=1300000000101, Title="站内信", Path="/dashboard/notice", Name="notice", Component="/home/notice/index", Icon="ele-Bell", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=101 },
-
-            // 建议此处Id范围之间放置具体业务应用菜单
-
-            new SysMenu{ Id=1310000000101, Pid=0, Title="系统管理", Path="/system", Name="system", Component="Layout", Icon="ele-Setting", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=10000 },
-
-            new SysMenu{ Id=1310000000111, Pid=1310000000101, Title="账号管理", Path="/system/user", Name="sysUser", Component="/system/user/index", Icon="ele-User", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000112, Pid=1310000000111, Title="查询", Permission="sysUser:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000113, Pid=1310000000111, Title="编辑", Permission="sysUser:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000114, Pid=1310000000111, Title="增加", Permission="sysUser:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000115, Pid=1310000000111, Title="删除", Permission="sysUser:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000116, Pid=1310000000111, Title="详情", Permission="sysUser:detail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000117, Pid=1310000000111, Title="授权角色", Permission="sysUser:grantRole", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000118, Pid=1310000000111, Title="重置密码", Permission="sysUser:resetPwd", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000119, Pid=1310000000111, Title="设置状态", Permission="sysUser:setStatus", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000120, Pid=1310000000111, Title="强制下线", Permission="sysOnlineUser:forceOffline", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000121, Pid=1310000000111, Title="解除锁定", Permission="sysUser:unlockLogin", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000131, Pid=1310000000101, Title="角色管理", Path="/system/role", Name="sysRole", Component="/system/role/index", Icon="ele-Help", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
-            new SysMenu{ Id=1310000000132, Pid=1310000000131, Title="查询", Permission="sysRole:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000133, Pid=1310000000131, Title="编辑", Permission="sysRole:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000134, Pid=1310000000131, Title="增加", Permission="sysRole:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000135, Pid=1310000000131, Title="删除", Permission="sysRole:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000136, Pid=1310000000131, Title="授权菜单", Permission="sysRole:grantMenu", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000137, Pid=1310000000131, Title="授权数据", Permission="sysRole:grantDataScope", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000138, Pid=1310000000131, Title="设置状态", Permission="sysRole:setStatus", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000141, Pid=1310000000101, Title="机构管理", Path="/system/org", Name="sysOrg", Component="/system/org/index", Icon="ele-OfficeBuilding", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
-            //new SysMenu{ Id=1310000000142, Pid=1310000000141, Title="查询", Permission="sysOrg:list", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000143, Pid=1310000000141, Title="编辑", Permission="sysOrg:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000144, Pid=1310000000141, Title="增加", Permission="sysOrg:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000145, Pid=1310000000141, Title="删除", Permission="sysOrg:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000151, Pid=1310000000101, Title="职位管理", Path="/system/pos", Name="sysPos", Component="/system/pos/index",Icon="ele-Mug", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
-            new SysMenu{ Id=1310000000152, Pid=1310000000151, Title="查询", Permission="sysPos:list", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000153, Pid=1310000000151, Title="编辑", Permission="sysPos:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000154, Pid=1310000000151, Title="增加", Permission="sysPos:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000155, Pid=1310000000151, Title="删除", Permission="sysPos:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000161, Pid=1310000000101, Title="个人中心", Path="/system/userCenter", Name="sysUserCenter", Component="/system/user/component/userCenter",Icon="ele-Medal", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=140 },
-            new SysMenu{ Id=1310000000162, Pid=1310000000161, Title="修改密码", Permission="sysUser:changePwd", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000163, Pid=1310000000161, Title="基本信息", Permission="sysUser:baseInfo", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000164, Pid=1310000000161, Title="电子签名", Permission="sysFile:uploadSignature", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000165, Pid=1310000000161, Title="上传头像", Permission="sysFile:uploadAvatar", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000171, Pid=1310000000101, Title="通知公告", Path="/system/notice", Name="sysNotice", Component="/system/notice/index",Icon="ele-Bell", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=150 },
-            new SysMenu{ Id=1310000000172, Pid=1310000000171, Title="查询", Permission="sysNotice:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000173, Pid=1310000000171, Title="编辑", Permission="sysNotice:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000174, Pid=1310000000171, Title="增加", Permission="sysNotice:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000175, Pid=1310000000171, Title="删除", Permission="sysNotice:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000176, Pid=1310000000171, Title="发布", Permission="sysNotice:public", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000177, Pid=1310000000171, Title="撤回", Permission="sysNotice:cancel", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000181, Pid=1310000000101, Title="三方账号", Path="/system/weChatUser", Name="sysWechatUser", Component="/system/weChatUser/index",Icon="ele-ChatDotRound", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=160 },
-            new SysMenu{ Id=1310000000182, Pid=1310000000181, Title="查询", Permission="sysWechatUser:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000183, Pid=1310000000181, Title="编辑", Permission="sysWechatUser:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000184, Pid=1310000000181, Title="增加", Permission="sysWechatUser:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000185, Pid=1310000000181, Title="删除", Permission="sysWechatUser:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000191, Pid=1310000000101, Title="AD域配置", Path="/system/ldap", Name="sysLdap", Component="/system/ldap/index",Icon="ele-Place", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=170 },
-            new SysMenu{ Id=1310000000192, Pid=1310000000191, Title="查询", Permission="sysLdap:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000193, Pid=1310000000191, Title="详情", Permission="sysLdap:detail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
-            new SysMenu{ Id=1310000000194, Pid=1310000000191, Title="编辑", Permission="sysLdap:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
-            new SysMenu{ Id=1310000000195, Pid=1310000000191, Title="增加", Permission="sysLdap:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
-            new SysMenu{ Id=1310000000196, Pid=1310000000191, Title="删除", Permission="sysLdap:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=140 },
-            new SysMenu{ Id=1310000000197, Pid=1310000000191, Title="同步域账户", Permission="sysLdap:syncUser", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=150 },
-            new SysMenu{ Id=1310000000198, Pid=1310000000191, Title="同步域组织", Permission="sysLdap:syncOrg", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=160 },
-
-            new SysMenu{ Id=1310000000301, Pid=0, Title="平台管理", Path="/platform", Name="platform", Component="Layout", Icon="ele-Menu", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=11000 },
-
-            new SysMenu{ Id=1310000000311, Pid=1310000000301, Title="租户管理", Path="/platform/tenant", Name="sysTenant", Component="/system/tenant/index", Icon="ele-School", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000312, Pid=1310000000311, Title="查询", Permission="sysTenant:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000313, Pid=1310000000311, Title="编辑", Permission="sysTenant:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000314, Pid=1310000000311, Title="增加", Permission="sysTenant:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000315, Pid=1310000000311, Title="删除", Permission="sysTenant:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000316, Pid=1310000000311, Title="授权菜单", Permission="sysTenant:grantMenu", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000317, Pid=1310000000311, Title="重置密码", Permission="sysTenant:resetPwd", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000318, Pid=1310000000311, Title="生成库", Permission="sysTenant:createDb", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000319, Pid=1310000000311, Title="设置状态", Permission="sysTenant:setStatus", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000321, Pid=1310000000301, Title="菜单管理", Path="/platform/menu", Name="sysMenu", Component="/system/menu/index", Icon="ele-Menu", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
-            new SysMenu{ Id=1310000000322, Pid=1310000000321, Title="查询", Permission="sysMenu:list", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000323, Pid=1310000000321, Title="编辑", Permission="sysMenu:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000324, Pid=1310000000321, Title="增加", Permission="sysMenu:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000325, Pid=1310000000321, Title="删除", Permission="sysMenu:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000331, Pid=1310000000301, Title="参数配置", Path="/platform/config", Name="sysConfig", Component="/system/config/index", Icon="ele-DocumentCopy", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
-            new SysMenu{ Id=1310000000332, Pid=1310000000331, Title="查询", Permission="sysConfig:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000333, Pid=1310000000331, Title="编辑", Permission="sysConfig:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000334, Pid=1310000000331, Title="增加", Permission="sysConfig:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000335, Pid=1310000000331, Title="删除", Permission="sysConfig:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000341, Pid=1310000000301, Title="字典管理", Path="/platform/dict", Name="sysDict", Component="/system/dict/index", Icon="ele-Collection", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
-            new SysMenu{ Id=1310000000342, Pid=1310000000341, Title="查询", Permission="sysDictType:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000343, Pid=1310000000341, Title="编辑", Permission="sysDictType:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000344, Pid=1310000000341, Title="增加", Permission="sysDictType:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000345, Pid=1310000000341, Title="删除", Permission="sysDictType:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000351, Pid=1310000000301, Title="任务调度", Path="/platform/job", Name="sysJob", Component="/system/job/index", Icon="ele-AlarmClock", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=140 },
-            new SysMenu{ Id=1310000000352, Pid=1310000000351, Title="查询", Permission="sysJob:pageJobDetail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000353, Pid=1310000000351, Title="编辑", Permission="sysJob:updateJobDetail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000354, Pid=1310000000351, Title="增加", Permission="sysJob:addJobDetail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000355, Pid=1310000000351, Title="删除", Permission="sysJob:deleteJobDetail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000361, Pid=1310000000301, Title="系统监控", Path="/platform/server", Name="sysServer", Component="/system/server/index", Icon="ele-Monitor", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=150 },
-
-            new SysMenu{ Id=1310000000371, Pid=1310000000301, Title="缓存管理", Path="/platform/cache", Name="sysCache", Component="/system/cache/index", Icon="ele-Loading", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=160 },
-            new SysMenu{ Id=1310000000372, Pid=1310000000371, Title="查询", Permission="sysCache:keyList", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000373, Pid=1310000000371, Title="删除", Permission="sysCache:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000381, Pid=1310000000301, Title="行政区域", Path="/platform/region", Name="sysRegion", Component="/system/region/index", Icon="ele-LocationInformation", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=170 },
-            new SysMenu{ Id=1310000000382, Pid=1310000000381, Title="查询", Permission="sysRegion:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000383, Pid=1310000000381, Title="编辑", Permission="sysRegion:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000384, Pid=1310000000381, Title="增加", Permission="sysRegion:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000385, Pid=1310000000381, Title="删除", Permission="sysRegion:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000386, Pid=1310000000381, Title="同步", Permission="sysRegion:sync", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000391, Pid=1310000000301, Title="文件管理", Path="/platform/file", Name="sysFile", Component="/system/file/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=180 },
-            new SysMenu{ Id=1310000000392, Pid=1310000000391, Title="查询", Permission="sysFile:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000393, Pid=1310000000391, Title="上传", Permission="sysFile:uploadFile", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000394, Pid=1310000000391, Title="下载", Permission="sysFile:downloadFile", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000395, Pid=1310000000391, Title="删除", Permission="sysFile:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000396, Pid=1310000000391, Title="编辑", Permission="sysFile:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2023-10-27 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000401, Pid=1310000000301, Title="打印模板", Path="/platform/print", Name="sysPrint", Component="/system/print/index", Icon="ele-Printer", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=190 },
-            new SysMenu{ Id=1310000000402, Pid=1310000000401, Title="查询", Permission="sysPrint:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000403, Pid=1310000000401, Title="编辑", Permission="sysPrint:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000404, Pid=1310000000401, Title="增加", Permission="sysPrint:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000405, Pid=1310000000401, Title="删除", Permission="sysPrint:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000411, Pid=1310000000301, Title="动态插件", Path="/platform/plugin", Name="sysPlugin", Component="/system/plugin/index", Icon="ele-Connection", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=200 },
-            new SysMenu{ Id=1310000000412, Pid=1310000000411, Title="查询", Permission="sysPlugin:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000413, Pid=1310000000411, Title="编辑", Permission="sysPlugin:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000414, Pid=1310000000411, Title="增加", Permission="sysPlugin:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000415, Pid=1310000000411, Title="删除", Permission="sysPlugin:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000421, Pid=1310000000301, Title="开放接口", Path="/platform/openAccess", Name="sysOpenAccess", Component="/system/openAccess/index", Icon="ele-Link", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=210 },
-            new SysMenu{ Id=1310000000422, Pid=1310000000421, Title="查询", Permission="sysOpenAccess:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000423, Pid=1310000000421, Title="编辑", Permission="sysOpenAccess:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000424, Pid=1310000000421, Title="增加", Permission="sysOpenAccess:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000425, Pid=1310000000421, Title="删除", Permission="sysOpenAccess:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000431, Pid=1310000000301, Title="系统配置", Path="/platform/infoSetting", Name="sysInfoSetting", Component="/system/infoSetting/index", Icon="ele-Setting", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=220 },
-
-            new SysMenu{ Id=1310000000501, Pid=0, Title="日志管理", Path="/log", Name="log", Component="Layout", Icon="ele-DocumentCopy", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=12000 },
-            new SysMenu{ Id=1310000000511, Pid=1310000000501, Title="访问日志", Path="/log/vislog", Name="sysVisLog", Component="/system/log/vislog/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000512, Pid=1310000000511, Title="查询", Permission="sysVislog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000513, Pid=1310000000511, Title="清空", Permission="sysVislog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000521, Pid=1310000000501, Title="操作日志", Path="/log/oplog", Name="sysOpLog", Component="/system/log/oplog/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
-            new SysMenu{ Id=1310000000522, Pid=1310000000521, Title="查询", Permission="sysOplog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000523, Pid=1310000000521, Title="清空", Permission="sysOplog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000524, Pid=1310000000521, Title="导出", Permission="sysOplog:export", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000531, Pid=1310000000501, Title="异常日志", Path="/log/exlog", Name="sysExLog", Component="/system/log/exlog/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
-            new SysMenu{ Id=1310000000532, Pid=1310000000531, Title="查询", Permission="sysExlog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000533, Pid=1310000000531, Title="清空", Permission="sysExlog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000534, Pid=1310000000531, Title="导出", Permission="sysExlog:export", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000541, Pid=1310000000501, Title="差异日志", Path="/log/difflog", Name="sysDiffLog", Component="/system/log/difflog/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
-            new SysMenu{ Id=1310000000542, Pid=1310000000541, Title="查询", Permission="sysDifflog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000543, Pid=1310000000541, Title="清空", Permission="sysDifflog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
-            new SysMenu{ Id=1310000000601, Pid=0, Title="开发工具", Path="/develop", Name="develop", Component="Layout", Icon="ele-Cpu", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=13000 },
-            new SysMenu{ Id=1310000000611, Pid=1310000000601, Title="库表管理", Path="/develop/database", Name="sysDatabase", Component="/system/database/index",Icon="ele-Coin", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000621, Pid=1310000000601, Title="代码生成", Path="/develop/codeGen", Name="sysCodeGen", Component="/system/codeGen/index", Icon="ele-Crop", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
-            new SysMenu{ Id=1310000000631, Pid=1310000000601, Title="表单设计", Path="/develop/formDes", Name="sysFormDes", Component="/system/formDes/index", Icon="ele-Edit", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
-            new SysMenu{ Id=1310000000641, Pid=1310000000601, Title="系统接口", Path="/develop/api", Name="sysApi", Component="layout/routerView/iframe", IsIframe=true, OutLink="http://localhost:5005", Icon="ele-Help", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
-
-            new SysMenu{ Id=1310000000701, Pid=0, Title="帮助文档", Path="/doc", Name="doc", Component="Layout", Icon="ele-Notebook", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=14000 },
-            new SysMenu{ Id=1310000000711, Pid=1310000000701, Title="后台教程", Path="/doc/furion", Name="sysFurion", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="https://furion.baiqian.ltd/", Icon="ele-Promotion", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-            new SysMenu{ Id=1310000000712, Pid=1310000000701, Title="前端教程", Path="/doc/element", Name="sysElement", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="https://element-plus.gitee.io/zh-CN/", Icon="ele-Position", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
-            new SysMenu{ Id=1310000000713, Pid=1310000000701, Title="SqlSugar", Path="/doc/SqlSugar", Name="sysSqlSugar", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="https://www.donet5.com/Home/Doc", Icon="ele-Coin", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
-        };
-    }
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 系统菜单表种子数据
+/// </summary>
+public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
+{
+    /// <summary>
+    /// 种子数据
+    /// </summary>
+    /// <returns></returns>
+    public IEnumerable<SysMenu> HasData()
+    {
+        return new[]
+        {
+            new SysMenu{ Id=1300000000101, Pid=0, Title="工作台", Path="/dashboard", Name="dashboard", Component="Layout", Icon="ele-HomeFilled", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=0 },
+            new SysMenu{ Id=1300000000111, Pid=1300000000101, Title="工作台", Path="/dashboard/home", Name="home", Component="/home/index", IsAffix=true, Icon="ele-HomeFilled", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1300000000121, Pid=1300000000101, Title="站内信", Path="/dashboard/notice", Name="notice", Component="/home/notice/index", Icon="ele-Bell", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=101 },
+
+            // 建议此处Id范围之间放置具体业务应用菜单
+
+            new SysMenu{ Id=1310000000101, Pid=0, Title="系统管理", Path="/system", Name="system", Component="Layout", Icon="ele-Setting", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=10000 },
+
+            new SysMenu{ Id=1310000000111, Pid=1310000000101, Title="账号管理", Path="/system/user", Name="sysUser", Component="/system/user/index", Icon="ele-User", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000112, Pid=1310000000111, Title="查询", Permission="sysUser:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000113, Pid=1310000000111, Title="编辑", Permission="sysUser:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000114, Pid=1310000000111, Title="增加", Permission="sysUser:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000115, Pid=1310000000111, Title="删除", Permission="sysUser:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000116, Pid=1310000000111, Title="详情", Permission="sysUser:detail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000117, Pid=1310000000111, Title="授权角色", Permission="sysUser:grantRole", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000118, Pid=1310000000111, Title="重置密码", Permission="sysUser:resetPwd", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000119, Pid=1310000000111, Title="设置状态", Permission="sysUser:setStatus", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000120, Pid=1310000000111, Title="强制下线", Permission="sysOnlineUser:forceOffline", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000121, Pid=1310000000111, Title="解除锁定", Permission="sysUser:unlockLogin", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000131, Pid=1310000000101, Title="角色管理", Path="/system/role", Name="sysRole", Component="/system/role/index", Icon="ele-Help", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
+            new SysMenu{ Id=1310000000132, Pid=1310000000131, Title="查询", Permission="sysRole:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000133, Pid=1310000000131, Title="编辑", Permission="sysRole:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000134, Pid=1310000000131, Title="增加", Permission="sysRole:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000135, Pid=1310000000131, Title="删除", Permission="sysRole:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000136, Pid=1310000000131, Title="授权菜单", Permission="sysRole:grantMenu", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000137, Pid=1310000000131, Title="授权数据", Permission="sysRole:grantDataScope", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000138, Pid=1310000000131, Title="设置状态", Permission="sysRole:setStatus", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000141, Pid=1310000000101, Title="机构管理", Path="/system/org", Name="sysOrg", Component="/system/org/index", Icon="ele-OfficeBuilding", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
+            //new SysMenu{ Id=1310000000142, Pid=1310000000141, Title="查询", Permission="sysOrg:list", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000143, Pid=1310000000141, Title="编辑", Permission="sysOrg:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000144, Pid=1310000000141, Title="增加", Permission="sysOrg:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000145, Pid=1310000000141, Title="删除", Permission="sysOrg:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000151, Pid=1310000000101, Title="职位管理", Path="/system/pos", Name="sysPos", Component="/system/pos/index",Icon="ele-Mug", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
+            new SysMenu{ Id=1310000000152, Pid=1310000000151, Title="查询", Permission="sysPos:list", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000153, Pid=1310000000151, Title="编辑", Permission="sysPos:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000154, Pid=1310000000151, Title="增加", Permission="sysPos:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000155, Pid=1310000000151, Title="删除", Permission="sysPos:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000161, Pid=1310000000101, Title="个人中心", Path="/system/userCenter", Name="sysUserCenter", Component="/system/user/component/userCenter",Icon="ele-Medal", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=140 },
+            new SysMenu{ Id=1310000000162, Pid=1310000000161, Title="修改密码", Permission="sysUser:changePwd", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000163, Pid=1310000000161, Title="基本信息", Permission="sysUser:baseInfo", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000164, Pid=1310000000161, Title="电子签名", Permission="sysFile:uploadSignature", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000165, Pid=1310000000161, Title="上传头像", Permission="sysFile:uploadAvatar", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000171, Pid=1310000000101, Title="通知公告", Path="/system/notice", Name="sysNotice", Component="/system/notice/index",Icon="ele-Bell", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=150 },
+            new SysMenu{ Id=1310000000172, Pid=1310000000171, Title="查询", Permission="sysNotice:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000173, Pid=1310000000171, Title="编辑", Permission="sysNotice:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000174, Pid=1310000000171, Title="增加", Permission="sysNotice:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000175, Pid=1310000000171, Title="删除", Permission="sysNotice:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000176, Pid=1310000000171, Title="发布", Permission="sysNotice:public", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000177, Pid=1310000000171, Title="撤回", Permission="sysNotice:cancel", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000181, Pid=1310000000101, Title="三方账号", Path="/system/weChatUser", Name="sysWechatUser", Component="/system/weChatUser/index",Icon="ele-ChatDotRound", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=160 },
+            new SysMenu{ Id=1310000000182, Pid=1310000000181, Title="查询", Permission="sysWechatUser:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000183, Pid=1310000000181, Title="编辑", Permission="sysWechatUser:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000184, Pid=1310000000181, Title="增加", Permission="sysWechatUser:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000185, Pid=1310000000181, Title="删除", Permission="sysWechatUser:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000191, Pid=1310000000101, Title="AD域配置", Path="/system/ldap", Name="sysLdap", Component="/system/ldap/index",Icon="ele-Place", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=170 },
+            new SysMenu{ Id=1310000000192, Pid=1310000000191, Title="查询", Permission="sysLdap:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000193, Pid=1310000000191, Title="详情", Permission="sysLdap:detail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
+            new SysMenu{ Id=1310000000194, Pid=1310000000191, Title="编辑", Permission="sysLdap:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
+            new SysMenu{ Id=1310000000195, Pid=1310000000191, Title="增加", Permission="sysLdap:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
+            new SysMenu{ Id=1310000000196, Pid=1310000000191, Title="删除", Permission="sysLdap:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=140 },
+            new SysMenu{ Id=1310000000197, Pid=1310000000191, Title="同步域账户", Permission="sysLdap:syncUser", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=150 },
+            new SysMenu{ Id=1310000000198, Pid=1310000000191, Title="同步域组织", Permission="sysLdap:syncOrg", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=160 },
+
+            new SysMenu{ Id=1310000000301, Pid=0, Title="平台管理", Path="/platform", Name="platform", Component="Layout", Icon="ele-Menu", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=11000 },
+
+            new SysMenu{ Id=1310000000311, Pid=1310000000301, Title="租户管理", Path="/platform/tenant", Name="sysTenant", Component="/system/tenant/index", Icon="ele-School", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000312, Pid=1310000000311, Title="查询", Permission="sysTenant:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000313, Pid=1310000000311, Title="编辑", Permission="sysTenant:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000314, Pid=1310000000311, Title="增加", Permission="sysTenant:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000315, Pid=1310000000311, Title="删除", Permission="sysTenant:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000316, Pid=1310000000311, Title="授权菜单", Permission="sysTenant:grantMenu", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000317, Pid=1310000000311, Title="重置密码", Permission="sysTenant:resetPwd", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000318, Pid=1310000000311, Title="生成库", Permission="sysTenant:createDb", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000319, Pid=1310000000311, Title="设置状态", Permission="sysTenant:setStatus", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000321, Pid=1310000000301, Title="菜单管理", Path="/platform/menu", Name="sysMenu", Component="/system/menu/index", Icon="ele-Menu", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
+            new SysMenu{ Id=1310000000322, Pid=1310000000321, Title="查询", Permission="sysMenu:list", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000323, Pid=1310000000321, Title="编辑", Permission="sysMenu:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000324, Pid=1310000000321, Title="增加", Permission="sysMenu:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000325, Pid=1310000000321, Title="删除", Permission="sysMenu:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000331, Pid=1310000000301, Title="参数配置", Path="/platform/config", Name="sysConfig", Component="/system/config/index", Icon="ele-DocumentCopy", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
+            new SysMenu{ Id=1310000000332, Pid=1310000000331, Title="查询", Permission="sysConfig:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000333, Pid=1310000000331, Title="编辑", Permission="sysConfig:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000334, Pid=1310000000331, Title="增加", Permission="sysConfig:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000335, Pid=1310000000331, Title="删除", Permission="sysConfig:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000341, Pid=1310000000301, Title="字典管理", Path="/platform/dict", Name="sysDict", Component="/system/dict/index", Icon="ele-Collection", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
+            new SysMenu{ Id=1310000000342, Pid=1310000000341, Title="查询", Permission="sysDictType:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000343, Pid=1310000000341, Title="编辑", Permission="sysDictType:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000344, Pid=1310000000341, Title="增加", Permission="sysDictType:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000345, Pid=1310000000341, Title="删除", Permission="sysDictType:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000351, Pid=1310000000301, Title="任务调度", Path="/platform/job", Name="sysJob", Component="/system/job/index", Icon="ele-AlarmClock", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=140 },
+            new SysMenu{ Id=1310000000352, Pid=1310000000351, Title="查询", Permission="sysJob:pageJobDetail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000353, Pid=1310000000351, Title="编辑", Permission="sysJob:updateJobDetail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000354, Pid=1310000000351, Title="增加", Permission="sysJob:addJobDetail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000355, Pid=1310000000351, Title="删除", Permission="sysJob:deleteJobDetail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000361, Pid=1310000000301, Title="系统监控", Path="/platform/server", Name="sysServer", Component="/system/server/index", Icon="ele-Monitor", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=150 },
+
+            new SysMenu{ Id=1310000000371, Pid=1310000000301, Title="缓存管理", Path="/platform/cache", Name="sysCache", Component="/system/cache/index", Icon="ele-Loading", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=160 },
+            new SysMenu{ Id=1310000000372, Pid=1310000000371, Title="查询", Permission="sysCache:keyList", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000373, Pid=1310000000371, Title="删除", Permission="sysCache:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000381, Pid=1310000000301, Title="行政区域", Path="/platform/region", Name="sysRegion", Component="/system/region/index", Icon="ele-LocationInformation", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=170 },
+            new SysMenu{ Id=1310000000382, Pid=1310000000381, Title="查询", Permission="sysRegion:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000383, Pid=1310000000381, Title="编辑", Permission="sysRegion:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000384, Pid=1310000000381, Title="增加", Permission="sysRegion:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000385, Pid=1310000000381, Title="删除", Permission="sysRegion:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000386, Pid=1310000000381, Title="同步", Permission="sysRegion:sync", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000391, Pid=1310000000301, Title="文件管理", Path="/platform/file", Name="sysFile", Component="/system/file/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=180 },
+            new SysMenu{ Id=1310000000392, Pid=1310000000391, Title="查询", Permission="sysFile:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000393, Pid=1310000000391, Title="上传", Permission="sysFile:uploadFile", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000394, Pid=1310000000391, Title="下载", Permission="sysFile:downloadFile", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000395, Pid=1310000000391, Title="删除", Permission="sysFile:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000396, Pid=1310000000391, Title="编辑", Permission="sysFile:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2023-10-27 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000401, Pid=1310000000301, Title="打印模板", Path="/platform/print", Name="sysPrint", Component="/system/print/index", Icon="ele-Printer", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=190 },
+            new SysMenu{ Id=1310000000402, Pid=1310000000401, Title="查询", Permission="sysPrint:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000403, Pid=1310000000401, Title="编辑", Permission="sysPrint:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000404, Pid=1310000000401, Title="增加", Permission="sysPrint:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000405, Pid=1310000000401, Title="删除", Permission="sysPrint:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000411, Pid=1310000000301, Title="动态插件", Path="/platform/plugin", Name="sysPlugin", Component="/system/plugin/index", Icon="ele-Connection", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=200 },
+            new SysMenu{ Id=1310000000412, Pid=1310000000411, Title="查询", Permission="sysPlugin:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000413, Pid=1310000000411, Title="编辑", Permission="sysPlugin:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000414, Pid=1310000000411, Title="增加", Permission="sysPlugin:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000415, Pid=1310000000411, Title="删除", Permission="sysPlugin:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000421, Pid=1310000000301, Title="开放接口", Path="/platform/openAccess", Name="sysOpenAccess", Component="/system/openAccess/index", Icon="ele-Link", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=210 },
+            new SysMenu{ Id=1310000000422, Pid=1310000000421, Title="查询", Permission="sysOpenAccess:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000423, Pid=1310000000421, Title="编辑", Permission="sysOpenAccess:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000424, Pid=1310000000421, Title="增加", Permission="sysOpenAccess:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000425, Pid=1310000000421, Title="删除", Permission="sysOpenAccess:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000431, Pid=1310000000301, Title="系统配置", Path="/platform/infoSetting", Name="sysInfoSetting", Component="/system/infoSetting/index", Icon="ele-Setting", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=220 },
+
+            new SysMenu{ Id=1310000000441, Pid=1310000000301, Title="微信支付", Path="/platform/wechatpay", Name="sysWechatPay", Component="/system/weChatPay/index", Icon="ele-Coin", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=230 },
+            new SysMenu{ Id=1310000000442, Pid=1310000000441, Title="微信支付下单Native", Permission="sysWechatPay:payTransactionNative", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000443, Pid=1310000000441, Title="查询退款信息", Permission="sysWechatPay:listRefund", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000444, Pid=1310000000441, Title="获取支付订单详情(本地库)", Permission="sysWechatPay:payInfo", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000445, Pid=1310000000441, Title="获取支付订单详情(微信接口)", Permission="sysWechatPay:payInfoFromWechat", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000446, Pid=1310000000441, Title="退款申请", Permission="sysWechatPay:refundDomestic", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000501, Pid=0, Title="日志管理", Path="/log", Name="log", Component="Layout", Icon="ele-DocumentCopy", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=12000 },
+            new SysMenu{ Id=1310000000511, Pid=1310000000501, Title="访问日志", Path="/log/vislog", Name="sysVisLog", Component="/system/log/vislog/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000512, Pid=1310000000511, Title="查询", Permission="sysVislog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000513, Pid=1310000000511, Title="清空", Permission="sysVislog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000521, Pid=1310000000501, Title="操作日志", Path="/log/oplog", Name="sysOpLog", Component="/system/log/oplog/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
+            new SysMenu{ Id=1310000000522, Pid=1310000000521, Title="查询", Permission="sysOplog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000523, Pid=1310000000521, Title="清空", Permission="sysOplog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000524, Pid=1310000000521, Title="导出", Permission="sysOplog:export", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000531, Pid=1310000000501, Title="异常日志", Path="/log/exlog", Name="sysExLog", Component="/system/log/exlog/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
+            new SysMenu{ Id=1310000000532, Pid=1310000000531, Title="查询", Permission="sysExlog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000533, Pid=1310000000531, Title="清空", Permission="sysExlog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000534, Pid=1310000000531, Title="导出", Permission="sysExlog:export", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000541, Pid=1310000000501, Title="差异日志", Path="/log/difflog", Name="sysDiffLog", Component="/system/log/difflog/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
+            new SysMenu{ Id=1310000000542, Pid=1310000000541, Title="查询", Permission="sysDifflog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000543, Pid=1310000000541, Title="清空", Permission="sysDifflog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+
+            new SysMenu{ Id=1310000000601, Pid=0, Title="开发工具", Path="/develop", Name="develop", Component="Layout", Icon="ele-Cpu", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=13000 },
+            new SysMenu{ Id=1310000000611, Pid=1310000000601, Title="库表管理", Path="/develop/database", Name="sysDatabase", Component="/system/database/index",Icon="ele-Coin", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000621, Pid=1310000000601, Title="代码生成", Path="/develop/codeGen", Name="sysCodeGen", Component="/system/codeGen/index", Icon="ele-Crop", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
+            new SysMenu{ Id=1310000000631, Pid=1310000000601, Title="表单设计", Path="/develop/formDes", Name="sysFormDes", Component="/system/formDes/index", Icon="ele-Edit", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
+            new SysMenu{ Id=1310000000641, Pid=1310000000601, Title="系统接口", Path="/develop/api", Name="sysApi", Component="layout/routerView/iframe", IsIframe=true, OutLink="http://localhost:5005", Icon="ele-Help", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
+
+            new SysMenu{ Id=1310000000701, Pid=0, Title="帮助文档", Path="/doc", Name="doc", Component="Layout", Icon="ele-Notebook", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=14000 },
+            new SysMenu{ Id=1310000000711, Pid=1310000000701, Title="框架教程", Path="/doc/admin", Name="sysAdmin", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="http://101.43.53.74:5050/", Icon="ele-Sunny", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000712, Pid=1310000000701, Title="后台教程", Path="/doc/furion", Name="sysFurion", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="https://furion.baiqian.ltd/", Icon="ele-Promotion", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
+            new SysMenu{ Id=1310000000713, Pid=1310000000701, Title="前端教程", Path="/doc/element", Name="sysElement", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="https://element-plus.gitee.io/zh-CN/", Icon="ele-Position", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
+            new SysMenu{ Id=1310000000714, Pid=1310000000701, Title="SqlSugar", Path="/doc/SqlSugar", Name="sysSqlSugar", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="https://www.donet5.com/Home/Doc", Icon="ele-Coin", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
+
+            new SysMenu{ Id=1310000000801, Pid=0, Title="关于项目", Path="/about", Name="about", Component="/about/index", Icon="ele-InfoFilled", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2023-03-12 00:00:00"), OrderNo=15000 },
+        };
+    }
 }

+ 5 - 0
Admin.NET/Admin.NET.Core/Service/Auth/Dto/LoginUserOutput.cs

@@ -95,4 +95,9 @@ public class LoginUserOutput
     /// 角色集合
     /// </summary>
     public List<long> RoleIds { get; set; }
+
+    /// <summary>
+    /// 水印文字
+    /// </summary>
+    public string WatermarkText { get; set; }
 }

+ 34 - 29
Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs

@@ -6,7 +6,6 @@
 
 using Furion.SpecificationDocument;
 using Lazy.Captcha.Core;
-using static SKIT.FlurlHttpClient.Wechat.Api.Models.ComponentTCBBatchCreateContainerServiceVersionRequest.Types;
 
 namespace Admin.NET.Core.Service;
 
@@ -63,10 +62,11 @@ public class SysAuthService : IDynamicApiController, ITransient
         //// 可以根据域名获取具体租户
         //var host = _httpContextAccessor.HttpContext.Request.Host;
 
-        // 判断密码错误次数(默认5次,缓存30分钟)
-        var keyErrorPasswordCount = $"{CacheConst.KeyErrorPasswordCount}{input.Account}";
-        var errorPasswordCount = _sysCacheService.Get<int>(keyErrorPasswordCount);
-        if (errorPasswordCount >= 5)
+        // 判断密码错误次数(缓存30分钟)
+        var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{input.Account}";
+        var passwordErrorTimes = _sysCacheService.Get<int>(keyPasswordErrorTimes);
+        var passwordMaxErrorTimes = await _sysConfigService.GetConfigValue<int>(CommonConst.SysPasswordMaxErrorTimes);
+        if (passwordErrorTimes >= passwordMaxErrorTimes)
             throw Oops.Oh(ErrorCodeEnum.D1027);
 
         // 是否开启验证码
@@ -106,19 +106,19 @@ public class SysAuthService : IDynamicApiController, ITransient
             var userLdap = await _sysUserLdap.GetFirstAsync(u => u.UserId == user.Id && u.TenantId == tenant.Id);
             if (userLdap == null)
             {
-                VerifyPassword(input, keyErrorPasswordCount, errorPasswordCount, user);
+                VerifyPassword(input, keyPasswordErrorTimes, passwordErrorTimes, user);
             }
             else if (!await _sysLdapService.AuthAccount(tenant.Id, userLdap.Account, input.Password))
             {
-                _sysCacheService.Set(keyErrorPasswordCount, ++errorPasswordCount, TimeSpan.FromMinutes(30));
+                _sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
                 throw Oops.Oh(ErrorCodeEnum.D1000);
             }
         }
         else
-            VerifyPassword(input, keyErrorPasswordCount, errorPasswordCount, user);
+            VerifyPassword(input, keyPasswordErrorTimes, passwordErrorTimes, user);
 
         // 登录成功则清空密码错误次数
-        _sysCacheService.Remove(keyErrorPasswordCount);
+        _sysCacheService.Remove(keyPasswordErrorTimes);
 
         return await CreateToken(user);
     }
@@ -127,16 +127,16 @@ public class SysAuthService : IDynamicApiController, ITransient
     /// 验证用户密码
     /// </summary>
     /// <param name="input"></param>
-    /// <param name="keyErrorPasswordCount"></param>
-    /// <param name="errorPasswordCount"></param>
+    /// <param name="keyPasswordErrorTimes"></param>
+    /// <param name="passwordErrorTimes"></param>
     /// <param name="user"></param>
-    private void VerifyPassword(LoginInput input, string keyErrorPasswordCount, int errorPasswordCount, SysUser user)
+    private void VerifyPassword(LoginInput input, string keyPasswordErrorTimes, int passwordErrorTimes, SysUser user)
     {
         if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString())
         {
             if (!user.Password.Equals(MD5Encryption.Encrypt(input.Password)))
             {
-                _sysCacheService.Set(keyErrorPasswordCount, ++errorPasswordCount, TimeSpan.FromMinutes(30));
+                _sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
                 throw Oops.Oh(ErrorCodeEnum.D1000);
             }
         }
@@ -144,7 +144,7 @@ public class SysAuthService : IDynamicApiController, ITransient
         {
             if (!CryptogramUtil.Decrypt(user.Password).Equals(input.Password))
             {
-                _sysCacheService.Set(keyErrorPasswordCount, ++errorPasswordCount, TimeSpan.FromMinutes(30));
+                _sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
                 throw Oops.Oh(ErrorCodeEnum.D1000);
             }
         }
@@ -208,7 +208,7 @@ public class SysAuthService : IDynamicApiController, ITransient
     /// <param name="user"></param>
     /// <returns></returns>
     [NonAction]
-    public virtual async Task<LoginOutput> CreateToken(SysUser user)
+    internal virtual async Task<LoginOutput> CreateToken(SysUser user)
     {
         // 单用户登录
         await _sysOnlineUserService.SingleLogin(user.Id);
@@ -237,6 +237,19 @@ public class SysAuthService : IDynamicApiController, ITransient
         // Swagger Knife4UI-AfterScript登录脚本
         // ke.global.setAllHeader('Authorization', 'Bearer ' + ke.response.headers['access-token']);
 
+        // 更新用户登录信息
+        user.LastLoginIp = _httpContextAccessor.HttpContext.GetRemoteIpAddressToIPv4(true);
+        (user.LastLoginAddress, double? longitude, double? latitude) = CommonUtil.GetIpAddress(user.LastLoginIp);
+        user.LastLoginTime = DateTime.Now;
+        user.LastLoginDevice = CommonUtil.GetClientDeviceInfo(_httpContextAccessor.HttpContext?.Request?.Headers?.UserAgent);
+        await _sysUserRep.AsUpdateable(user).UpdateColumns(u => new
+        {
+            u.LastLoginIp,
+            u.LastLoginAddress,
+            u.LastLoginTime,
+            u.LastLoginDevice,
+        }).ExecuteCommandAsync();
+
         return new LoginOutput
         {
             AccessToken = accessToken,
@@ -261,7 +274,10 @@ public class SysAuthService : IDynamicApiController, ITransient
         // 获取角色集合
         var roleIds = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysUserRole>>().AsQueryable()
             .Where(u => u.UserId == user.Id).Select(u => u.RoleId).ToListAsync();
-
+        // 获取水印文字(若系统水印为空则全局为空)
+        var watermarkText = await _sysConfigService.GetConfigValue<string>("sys_web_watermark");
+        if (!string.IsNullOrWhiteSpace(watermarkText))
+            watermarkText += $"-{user.RealName}"; // $"-{user.RealName}-{_httpContextAccessor.HttpContext.GetRemoteIpAddressToIPv4(true)}-{DateTime.Now}";
         return new LoginUserOutput
         {
             Id = user.Id,
@@ -279,7 +295,8 @@ public class SysAuthService : IDynamicApiController, ITransient
             OrgType = org?.Type,
             PosName = pos?.Name,
             Buttons = buttons,
-            RoleIds = roleIds
+            RoleIds = roleIds,
+            WatermarkText = watermarkText
         };
     }
 
@@ -321,18 +338,6 @@ public class SysAuthService : IDynamicApiController, ITransient
         return new { SecondVerEnabled = secondVerEnabled, CaptchaEnabled = captchaEnabled };
     }
 
-    /// <summary>
-    /// 获取水印配置 🔖
-    /// </summary>
-    /// <returns></returns>
-    [SuppressMonitor]
-    [DisplayName("获取水印配置")]
-    public async Task<dynamic> GetWatermarkConfig()
-    {
-        var watermarkEnabled = await _sysConfigService.GetConfigValue<bool>(CommonConst.SysWatermark);
-        return new { WatermarkEnabled = watermarkEnabled };
-    }
-
     /// <summary>
     /// 获取验证码 🔖
     /// </summary>

+ 13 - 21
Admin.NET/Admin.NET.Core/Service/Auth/SysLdapService.cs

@@ -14,19 +14,11 @@ namespace Admin.NET.Core;
 [ApiDescriptionSettings(Order = 496)]
 public class SysLdapService : IDynamicApiController, ITransient
 {
-    private SqlSugarRepository<SysLdap> sysLdapRep = null;
+    private readonly SqlSugarRepository<SysLdap> _sysLdapRep;
 
-    public SysLdapService()
+    public SysLdapService(SqlSugarRepository<SysLdap> sysLdapRep)
     {
-    }
-
-    public SqlSugarRepository<SysLdap> SysLdapRep
-    {
-        get
-        {
-            sysLdapRep ??= App.GetRequiredService<SqlSugarRepository<SysLdap>>();
-            return sysLdapRep;
-        }
+        _sysLdapRep = sysLdapRep;
     }
 
     /// <summary>
@@ -37,7 +29,7 @@ public class SysLdapService : IDynamicApiController, ITransient
     [DisplayName("获取系统域登录配置分页列表")]
     public async Task<SqlSugarPagedList<SysLdap>> Page(SysLdapInput input)
     {
-        return await SysLdapRep.AsQueryable()
+        return await _sysLdapRep.AsQueryable()
             .WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), u => u.Host.Contains(input.SearchKey.Trim()))
             .WhereIF(!string.IsNullOrWhiteSpace(input.Host), u => u.Host.Contains(input.Host.Trim()))
             .OrderBy(u => u.CreateTime, OrderByType.Desc)
@@ -55,7 +47,7 @@ public class SysLdapService : IDynamicApiController, ITransient
     {
         var entity = input.Adapt<SysLdap>();
         entity.BindPass = CryptogramUtil.Encrypt(input.BindPass);
-        await SysLdapRep.InsertAsync(entity);
+        await _sysLdapRep.InsertAsync(entity);
         return entity.Id;
     }
 
@@ -74,7 +66,7 @@ public class SysLdapService : IDynamicApiController, ITransient
             entity.BindPass = CryptogramUtil.Encrypt(input.BindPass); // 加密
         }
 
-        await SysLdapRep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
+        await _sysLdapRep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
     }
 
     /// <summary>
@@ -86,8 +78,8 @@ public class SysLdapService : IDynamicApiController, ITransient
     [DisplayName("删除系统域登录配置")]
     public async Task Delete(DeleteSysLdapInput input)
     {
-        var entity = await SysLdapRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
-        await SysLdapRep.FakeDeleteAsync(entity); // 假删除
+        var entity = await _sysLdapRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+        await _sysLdapRep.FakeDeleteAsync(entity); // 假删除
         //await _rep.DeleteAsync(entity); // 真删除
     }
 
@@ -99,7 +91,7 @@ public class SysLdapService : IDynamicApiController, ITransient
     [DisplayName("获取系统域登录配置详情")]
     public async Task<SysLdap> GetDetail([FromQuery] DetailSysLdapInput input)
     {
-        return await SysLdapRep.GetFirstAsync(u => u.Id == input.Id);
+        return await _sysLdapRep.GetFirstAsync(u => u.Id == input.Id);
     }
 
     /// <summary>
@@ -109,7 +101,7 @@ public class SysLdapService : IDynamicApiController, ITransient
     [DisplayName("获取系统域登录配置列表")]
     public async Task<List<SysLdap>> GetList()
     {
-        return await SysLdapRep.AsQueryable().Select<SysLdap>().ToListAsync();
+        return await _sysLdapRep.AsQueryable().Select<SysLdap>().ToListAsync();
     }
 
     /// <summary>
@@ -122,7 +114,7 @@ public class SysLdapService : IDynamicApiController, ITransient
     [NonAction]
     public async Task<bool> AuthAccount(long tenantId, string account, string password)
     {
-        var sysLdap = await SysLdapRep.GetFirstAsync(u => u.TenantId == tenantId) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+        var sysLdap = await _sysLdapRep.GetFirstAsync(u => u.TenantId == tenantId) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
         var ldapConn = new LdapConnection();
         try
         {
@@ -170,7 +162,7 @@ public class SysLdapService : IDynamicApiController, ITransient
     [DisplayName("同步域用户")]
     public async Task SyncUser(SyncSysLdapInput input)
     {
-        var sysLdap = await SysLdapRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+        var sysLdap = await _sysLdapRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
         var ldapConn = new LdapConnection();
         try
         {
@@ -302,7 +294,7 @@ public class SysLdapService : IDynamicApiController, ITransient
     [DisplayName("同步域组织")]
     public async Task SyncDept(SyncSysLdapInput input)
     {
-        var sysLdap = await SysLdapRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+        var sysLdap = await _sysLdapRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
         var ldapConn = new LdapConnection();
         try
         {

+ 23 - 23
Admin.NET/Admin.NET.Core/Service/Cache/SysCacheService.cs

@@ -14,12 +14,12 @@ namespace Admin.NET.Core.Service;
 [ApiDescriptionSettings(Order = 400)]
 public class SysCacheService : IDynamicApiController, ISingleton
 {
-    private readonly ICache _cache;
+    private static ICacheProvider _cacheProvider;
     private readonly CacheOptions _cacheOptions;
 
-    public SysCacheService(ICache cache, IOptions<CacheOptions> cacheOptions)
+    public SysCacheService(ICacheProvider cacheProvider, IOptions<CacheOptions> cacheOptions)
     {
-        _cache = cache;
+        _cacheProvider = cacheProvider;
         _cacheOptions = cacheOptions.Value;
     }
 
@@ -30,9 +30,9 @@ public class SysCacheService : IDynamicApiController, ISingleton
     [DisplayName("获取缓存键名集合")]
     public List<string> GetKeyList()
     {
-        return _cache == Cache.Default
-            ? _cache.Keys.Where(u => u.StartsWith(_cacheOptions.Prefix)).Select(u => u[_cacheOptions.Prefix.Length..]).OrderBy(u => u).ToList()
-            : ((FullRedis)_cache).Search($"{_cacheOptions.Prefix}*", int.MaxValue).Select(u => u[_cacheOptions.Prefix.Length..]).OrderBy(u => u).ToList();
+        return _cacheProvider.Cache == Cache.Default
+            ? _cacheProvider.Cache.Keys.Where(u => u.StartsWith(_cacheOptions.Prefix)).Select(u => u[_cacheOptions.Prefix.Length..]).OrderBy(u => u).ToList()
+            : ((FullRedis)_cacheProvider.Cache).Search($"{_cacheOptions.Prefix}*", int.MaxValue).Select(u => u[_cacheOptions.Prefix.Length..]).OrderBy(u => u).ToList();
     }
 
     /// <summary>
@@ -45,7 +45,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
     public bool Set(string key, object value)
     {
         if (string.IsNullOrWhiteSpace(key)) return false;
-        return _cache.Set($"{_cacheOptions.Prefix}{key}", value);
+        return _cacheProvider.Cache.Set($"{_cacheOptions.Prefix}{key}", value);
     }
 
     /// <summary>
@@ -59,7 +59,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
     public bool Set(string key, object value, TimeSpan expire)
     {
         if (string.IsNullOrWhiteSpace(key)) return false;
-        return _cache.Set($"{_cacheOptions.Prefix}{key}", value, expire);
+        return _cacheProvider.Cache.Set($"{_cacheOptions.Prefix}{key}", value, expire);
     }
 
     /// <summary>
@@ -71,7 +71,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
     [NonAction]
     public T Get<T>(string key)
     {
-        return _cache.Get<T>($"{_cacheOptions.Prefix}{key}");
+        return _cacheProvider.Cache.Get<T>($"{_cacheOptions.Prefix}{key}");
     }
 
     /// <summary>
@@ -83,7 +83,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
     [DisplayName("删除缓存")]
     public int Remove(string key)
     {
-        return _cache.Remove($"{_cacheOptions.Prefix}{key}");
+        return _cacheProvider.Cache.Remove($"{_cacheOptions.Prefix}{key}");
     }
 
     /// <summary>
@@ -94,7 +94,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
     [NonAction]
     public bool ExistKey(string key)
     {
-        return _cache.ContainsKey($"{_cacheOptions.Prefix}{key}");
+        return _cacheProvider.Cache.ContainsKey($"{_cacheOptions.Prefix}{key}");
     }
 
     /// <summary>
@@ -106,10 +106,10 @@ public class SysCacheService : IDynamicApiController, ISingleton
     [DisplayName("根据键名前缀删除缓存")]
     public int RemoveByPrefixKey(string prefixKey)
     {
-        var delKeys = _cache == Cache.Default
-            ? _cache.Keys.Where(u => u.StartsWith($"{_cacheOptions.Prefix}{prefixKey}")).ToArray()
-            : ((FullRedis)_cache).Search($"{_cacheOptions.Prefix}{prefixKey}*", int.MaxValue).ToArray();
-        return _cache.Remove(delKeys);
+        var delKeys = _cacheProvider.Cache == Cache.Default
+            ? _cacheProvider.Cache.Keys.Where(u => u.StartsWith($"{_cacheOptions.Prefix}{prefixKey}")).ToArray()
+            : ((FullRedis)_cacheProvider.Cache).Search($"{_cacheOptions.Prefix}{prefixKey}*", int.MaxValue).ToArray();
+        return _cacheProvider.Cache.Remove(delKeys);
     }
 
     /// <summary>
@@ -120,9 +120,9 @@ public class SysCacheService : IDynamicApiController, ISingleton
     [DisplayName("根据键名前缀获取键名集合")]
     public List<string> GetKeysByPrefixKey(string prefixKey)
     {
-        return _cache == Cache.Default
-            ? _cache.Keys.Where(u => u.StartsWith($"{_cacheOptions.Prefix}{prefixKey}")).Select(u => u[_cacheOptions.Prefix.Length..]).ToList()
-            : ((FullRedis)_cache).Search($"{_cacheOptions.Prefix}{prefixKey}*", int.MaxValue).Select(u => u[_cacheOptions.Prefix.Length..]).ToList();
+        return _cacheProvider.Cache == Cache.Default
+            ? _cacheProvider.Cache.Keys.Where(u => u.StartsWith($"{_cacheOptions.Prefix}{prefixKey}")).Select(u => u[_cacheOptions.Prefix.Length..]).ToList()
+            : ((FullRedis)_cacheProvider.Cache).Search($"{_cacheOptions.Prefix}{prefixKey}*", int.MaxValue).Select(u => u[_cacheOptions.Prefix.Length..]).ToList();
     }
 
     /// <summary>
@@ -133,9 +133,9 @@ public class SysCacheService : IDynamicApiController, ISingleton
     [DisplayName("获取缓存值")]
     public object GetValue(string key)
     {
-        return _cache == Cache.Default
-            ? _cache.Get<object>($"{_cacheOptions.Prefix}{key}")
-            : _cache.Get<string>($"{_cacheOptions.Prefix}{key}");
+        return _cacheProvider.Cache == Cache.Default
+            ? _cacheProvider.Cache.Get<object>($"{_cacheOptions.Prefix}{key}")
+            : _cacheProvider.Cache.Get<string>($"{_cacheOptions.Prefix}{key}");
     }
 
     /// <summary>
@@ -150,7 +150,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
     public T GetOrAdd<T>(string key, Func<string, T> callback, int expire = -1)
     {
         if (string.IsNullOrWhiteSpace(key)) return default;
-        return _cache.GetOrAdd($"{_cacheOptions.Prefix}{key}", callback, expire);
+        return _cacheProvider.Cache.GetOrAdd($"{_cacheOptions.Prefix}{key}", callback, expire);
     }
 
     /// <summary>
@@ -162,7 +162,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
     [NonAction]
     public RedisHash<string, T> GetHashMap<T>(string key)
     {
-        return _cache.GetDictionary<T>(key) as RedisHash<string, T>;
+        return _cacheProvider.Cache.GetDictionary<T>(key) as RedisHash<string, T>;
     }
 
     /// <summary>

+ 1 - 1
Admin.NET/Admin.NET.Core/Service/CodeGen/SysCodeGenConfigService.cs

@@ -112,7 +112,7 @@ public class SysCodeGenConfigService : IDynamicApiController, ITransient
             codeGenConfig.PropertyName = tableColumn.PropertyName;// 实体属性名
             codeGenConfig.ColumnLength = tableColumn.ColumnLength;// 长度
             codeGenConfig.ColumnComment = tableColumn.ColumnComment;
-            codeGenConfig.NetType = tableColumn.DataType;
+            codeGenConfig.NetType = tableColumn.NetType;
             codeGenConfig.WhetherRetract = YesNoEnum.N.ToString();
 
             // 生成代码时,主键并不是必要输入项,故一定要排除主键字段

+ 9 - 4
Admin.NET/Admin.NET.Core/Service/CodeGen/SysCodeGenService.cs

@@ -248,10 +248,15 @@ public class SysCodeGenService : IDynamicApiController, ITransient
         var types = new List<Type>();
         if (_codeGenOptions.EntityAssemblyNames != null)
         {
-            foreach (var assemblyName in _codeGenOptions.EntityAssemblyNames)
+            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+            foreach (var assembly in assemblies)
             {
-                Assembly asm = Assembly.Load(assemblyName);
-                types.AddRange(asm.GetExportedTypes().ToList());
+                var assemblyName = assembly.GetName().Name;
+                if (_codeGenOptions.EntityAssemblyNames.Contains(assemblyName) || _codeGenOptions.EntityAssemblyNames.Any(name => assemblyName.Contains(name)))
+                {
+                    Assembly asm = Assembly.Load(assemblyName);
+                    types.AddRange(asm.GetExportedTypes().ToList());
+                }
             }
         }
         bool IsMyAttribute(Attribute[] o)
@@ -343,7 +348,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
             PrintType = input.PrintType,
             PrintName = input.PrintName,
         };
-        //模板目录
+        // 模板目录
         var templatePathList = GetTemplatePathList(input);
         var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "Template");
 

+ 16 - 0
Admin.NET/Admin.NET.Core/Service/Config/Dto/ConfigInput.cs

@@ -38,4 +38,20 @@ public class UpdateConfigInput : AddConfigInput
 
 public class DeleteConfigInput : BaseIdInput
 {
+}
+
+/// <summary>
+/// 批量配置参数输入
+/// </summary>
+public class BatchConfigInput
+{
+    /// <summary>
+    /// 编码
+    /// </summary>
+    public string Code { get; set; }
+
+    /// <summary>
+    /// 属性值
+    /// </summary>
+    public string Value { get; set; }
 }

+ 39 - 27
Admin.NET/Admin.NET.Core/Service/Config/SysConfigService.cs

@@ -13,20 +13,13 @@ namespace Admin.NET.Core.Service;
 public class SysConfigService : IDynamicApiController, ITransient
 {
     private readonly SysCacheService _sysCacheService;
-    private SqlSugarRepository<SysConfig> sysConfigRep = null;
+    private readonly SqlSugarRepository<SysConfig> _sysConfigRep;
 
-    public SysConfigService(SysCacheService sysCacheService)
+    public SysConfigService(SysCacheService sysCacheService,
+        SqlSugarRepository<SysConfig> sysConfigRep)
     {
         _sysCacheService = sysCacheService;
-    }
-
-    public SqlSugarRepository<SysConfig> SysConfigRep
-    {
-        get
-        {
-            sysConfigRep ??= App.GetRequiredService<SqlSugarRepository<SysConfig>>();
-            return sysConfigRep;
-        }
+        _sysConfigRep = sysConfigRep;
     }
 
     /// <summary>
@@ -37,7 +30,7 @@ public class SysConfigService : IDynamicApiController, ITransient
     [DisplayName("获取参数配置分页列表")]
     public async Task<SqlSugarPagedList<SysConfig>> Page(PageConfigInput input)
     {
-        return await SysConfigRep.AsQueryable()
+        return await _sysConfigRep.AsQueryable()
             .Where(u => u.GroupCode != "WebConfig") // 不显示 WebConfig 分组
             .WhereIF(!string.IsNullOrWhiteSpace(input.Name?.Trim()), u => u.Name.Contains(input.Name))
             .WhereIF(!string.IsNullOrWhiteSpace(input.Code?.Trim()), u => u.Code.Contains(input.Code))
@@ -51,9 +44,11 @@ public class SysConfigService : IDynamicApiController, ITransient
     /// </summary>
     /// <returns></returns>
     [DisplayName("获取参数配置列表")]
-    public async Task<List<SysConfig>> GetList()
+    public async Task<List<SysConfig>> List(PageConfigInput input)
     {
-        return await SysConfigRep.GetListAsync();
+        return await _sysConfigRep.AsQueryable()
+            .WhereIF(!string.IsNullOrWhiteSpace(input.GroupCode?.Trim()), u => u.GroupCode.Equals(input.GroupCode))
+            .ToListAsync();
     }
 
     /// <summary>
@@ -65,11 +60,11 @@ public class SysConfigService : IDynamicApiController, ITransient
     [DisplayName("增加参数配置")]
     public async Task AddConfig(AddConfigInput input)
     {
-        var isExist = await SysConfigRep.IsAnyAsync(u => u.Name == input.Name || u.Code == input.Code);
+        var isExist = await _sysConfigRep.IsAnyAsync(u => u.Name == input.Name || u.Code == input.Code);
         if (isExist)
             throw Oops.Oh(ErrorCodeEnum.D9000);
 
-        await SysConfigRep.InsertAsync(input.Adapt<SysConfig>());
+        await _sysConfigRep.InsertAsync(input.Adapt<SysConfig>());
     }
 
     /// <summary>
@@ -81,12 +76,12 @@ public class SysConfigService : IDynamicApiController, ITransient
     [DisplayName("更新参数配置")]
     public async Task UpdateConfig(UpdateConfigInput input)
     {
-        var isExist = await SysConfigRep.IsAnyAsync(u => (u.Name == input.Name || u.Code == input.Code) && u.Id != input.Id);
+        var isExist = await _sysConfigRep.IsAnyAsync(u => (u.Name == input.Name || u.Code == input.Code) && u.Id != input.Id);
         if (isExist)
             throw Oops.Oh(ErrorCodeEnum.D9000);
 
         var config = input.Adapt<SysConfig>();
-        await SysConfigRep.AsUpdateable(config).IgnoreColumns(true).ExecuteCommandAsync();
+        await _sysConfigRep.AsUpdateable(config).IgnoreColumns(true).ExecuteCommandAsync();
 
         _sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}");
     }
@@ -100,11 +95,11 @@ public class SysConfigService : IDynamicApiController, ITransient
     [DisplayName("删除参数配置")]
     public async Task DeleteConfig(DeleteConfigInput input)
     {
-        var config = await SysConfigRep.GetFirstAsync(u => u.Id == input.Id);
+        var config = await _sysConfigRep.GetFirstAsync(u => u.Id == input.Id);
         if (config.SysFlag == YesNoEnum.Y) // 禁止删除系统参数
             throw Oops.Oh(ErrorCodeEnum.D9001);
 
-        await SysConfigRep.DeleteAsync(config);
+        await _sysConfigRep.DeleteAsync(config);
 
         _sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}");
     }
@@ -120,11 +115,11 @@ public class SysConfigService : IDynamicApiController, ITransient
     {
         foreach (var id in ids)
         {
-            var config = await SysConfigRep.GetFirstAsync(u => u.Id == id);
+            var config = await _sysConfigRep.GetFirstAsync(u => u.Id == id);
             if (config.SysFlag == YesNoEnum.Y) // 禁止删除系统参数
                 continue;
 
-            await SysConfigRep.DeleteAsync(config);
+            await _sysConfigRep.DeleteAsync(config);
 
             _sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}");
         }
@@ -138,7 +133,7 @@ public class SysConfigService : IDynamicApiController, ITransient
     [DisplayName("获取参数配置详情")]
     public async Task<SysConfig> GetDetail([FromQuery] ConfigInput input)
     {
-        return await SysConfigRep.GetFirstAsync(u => u.Id == input.Id);
+        return await _sysConfigRep.GetFirstAsync(u => u.Id == input.Id);
     }
 
     /// <summary>
@@ -154,7 +149,7 @@ public class SysConfigService : IDynamicApiController, ITransient
         var value = _sysCacheService.Get<string>($"{CacheConst.KeyConfig}{code}");
         if (string.IsNullOrEmpty(value))
         {
-            var config = await SysConfigRep.GetFirstAsync(u => u.Code == code);
+            var config = await _sysConfigRep.CopyNew().GetFirstAsync(u => u.Code == code);
             value = config != null ? config.Value : default;
             _sysCacheService.Set($"{CacheConst.KeyConfig}{code}", value);
         }
@@ -171,11 +166,11 @@ public class SysConfigService : IDynamicApiController, ITransient
     [NonAction]
     public async Task UpdateConfigValue(string code, string value)
     {
-        var config = await SysConfigRep.GetFirstAsync(u => u.Code == code);
+        var config = await _sysConfigRep.GetFirstAsync(u => u.Code == code);
         if (config == null) return;
 
         config.Value = value;
-        await SysConfigRep.AsUpdateable(config).ExecuteCommandAsync();
+        await _sysConfigRep.AsUpdateable(config).ExecuteCommandAsync();
 
         _sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}");
     }
@@ -187,7 +182,7 @@ public class SysConfigService : IDynamicApiController, ITransient
     [DisplayName("获取分组列表")]
     public async Task<List<string>> GetGroupList()
     {
-        return await SysConfigRep.AsQueryable()
+        return await _sysConfigRep.AsQueryable()
             .Where(u => u.GroupCode != "WebConfig") // 不显示 WebConfig 分组
             .GroupBy(u => u.GroupCode)
             .Select(u => u.GroupCode).ToListAsync();
@@ -217,6 +212,22 @@ public class SysConfigService : IDynamicApiController, ITransient
         return refreshTokenExpire == 0 ? 40 : refreshTokenExpire;
     }
 
+    /// <summary>
+    /// 批量更新参数配置值
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [ApiDescriptionSettings(Name = "BatchUpdate"), HttpPost]
+    [DisplayName("批量更新参数配置值")]
+    public async Task BatchUpdateConfig(List<BatchConfigInput> input)
+    {
+        foreach (var Config in input)
+        {
+            await _sysConfigRep.AsUpdateable().SetColumns(u => u.Value == Config.Value).Where(u => u.Code == Config.Code).ExecuteCommandAsync();
+            _sysCacheService.Remove($"{CacheConst.KeyConfig}{Config.Code}");
+        }
+    }
+
     /// <summary>
     /// 获取系统信息 🔖
     /// </summary>
@@ -234,6 +245,7 @@ public class SysConfigService : IDynamicApiController, ITransient
         var sysCopyright = await GetConfigValue<string>("sys_web_copyright");
         var sysIcp = await GetConfigValue<string>("sys_web_icp");
         var sysIcpUrl = await GetConfigValue<string>("sys_web_icpUrl");
+
         return new
         {
             SysLogo = sysLogo,

+ 2 - 1
Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs

@@ -254,7 +254,8 @@ public class SysDatabaseService : IDynamicApiController, ITransient
         {
             if (db.DbMaintenance.IsAnyTableRemark(input.TableName))
                 db.DbMaintenance.DeleteTableRemark(input.TableName);
-            else
+
+            if (!string.IsNullOrWhiteSpace(input.Description))
                 db.DbMaintenance.AddTableRemark(input.TableName, input.Description);
         }
         catch (NotSupportedException ex)

+ 46 - 11
Admin.NET/Admin.NET.Core/Service/Dict/SysDictDataService.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -13,11 +13,14 @@ namespace Admin.NET.Core.Service;
 [AllowAnonymous]
 public class SysDictDataService : IDynamicApiController, ITransient
 {
+    private readonly SysCacheService _sysCacheService;
     private readonly SqlSugarRepository<SysDictData> _sysDictDataRep;
 
-    public SysDictDataService(SqlSugarRepository<SysDictData> sysDictDataRep)
+    public SysDictDataService(SqlSugarRepository<SysDictData> sysDictDataRep
+        , SysCacheService sysCacheService)
     {
         _sysDictDataRep = sysDictDataRep;
+        _sysCacheService = sysCacheService;
     }
 
     /// <summary>
@@ -40,6 +43,7 @@ public class SysDictDataService : IDynamicApiController, ITransient
     /// 获取字典值列表 🔖
     /// </summary>
     /// <returns></returns>
+    [UnitOfWork]
     [DisplayName("获取字典值列表")]
     public async Task<List<SysDictData>> GetList([FromQuery] GetDataDictDataInput input)
     {
@@ -67,6 +71,7 @@ public class SysDictDataService : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
+    [UnitOfWork]
     [ApiDescriptionSettings(Name = "Update"), HttpPost]
     [DisplayName("更新字典值")]
     public async Task UpdateDictData(UpdateDictDataInput input)
@@ -77,6 +82,9 @@ public class SysDictDataService : IDynamicApiController, ITransient
         isExist = await _sysDictDataRep.IsAnyAsync(u => u.Code == input.Code && u.DictTypeId == input.DictTypeId && u.Id != input.Id);
         if (isExist) throw Oops.Oh(ErrorCodeEnum.D3003);
 
+        var dictTypeCode = await _sysDictDataRep.AsQueryable().Where(u => u.DictTypeId == input.DictTypeId).Select(u => u.DictType.Code).FirstAsync();
+        _sysCacheService.Remove($"{CacheConst.KeyDict}{dictTypeCode}");
+
         await _sysDictDataRep.UpdateAsync(input.Adapt<SysDictData>());
     }
 
@@ -85,6 +93,7 @@ public class SysDictDataService : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
+    [UnitOfWork]
     [ApiDescriptionSettings(Name = "Delete"), HttpPost]
     [DisplayName("删除字典值")]
     public async Task DeleteDictData(DeleteDictDataInput input)
@@ -93,6 +102,9 @@ public class SysDictDataService : IDynamicApiController, ITransient
         if (dictData == null)
             throw Oops.Oh(ErrorCodeEnum.D3004);
 
+        var dictTypeCode = await _sysDictDataRep.AsQueryable().Where(u => u.DictTypeId == dictData.Id).Select(u => u.DictType.Code).FirstAsync();
+        _sysCacheService.Remove($"{CacheConst.KeyDict}{dictTypeCode}");
+
         await _sysDictDataRep.DeleteAsync(dictData);
     }
 
@@ -112,6 +124,7 @@ public class SysDictDataService : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
+    [UnitOfWork]
     [DisplayName("修改字典值状态")]
     public async Task SetStatus(DictDataInput input)
     {
@@ -122,6 +135,9 @@ public class SysDictDataService : IDynamicApiController, ITransient
         if (!Enum.IsDefined(typeof(StatusEnum), input.Status))
             throw Oops.Oh(ErrorCodeEnum.D3005);
 
+        var dictTypeCode = await _sysDictDataRep.AsQueryable().Where(u => u.DictTypeId == dictData.Id).Select(u => u.DictType.Code).FirstAsync();
+        _sysCacheService.Remove($"{CacheConst.KeyDict}{dictTypeCode}");
+
         dictData.Status = input.Status;
         await _sysDictDataRep.UpdateAsync(dictData);
     }
@@ -134,10 +150,20 @@ public class SysDictDataService : IDynamicApiController, ITransient
     [NonAction]
     public async Task<List<SysDictData>> GetDictDataListByDictTypeId(long dictTypeId)
     {
-        return await _sysDictDataRep.AsQueryable()
-            .Where(u => u.DictTypeId == dictTypeId)
-            .OrderBy(u => new { u.OrderNo, u.Code })
-            .ToListAsync();
+        var dictType = await _sysDictDataRep.ChangeRepository<SqlSugarRepository<SysDictType>>().GetByIdAsync(dictTypeId);
+        var dictDataList = _sysCacheService.Get<List<SysDictData>>($"{CacheConst.KeyDict}{dictTypeId}");
+
+        if (dictDataList == null)
+        {
+            dictDataList = await _sysDictDataRep.AsQueryable()
+                .Where(u => u.DictTypeId == dictTypeId)
+                .OrderBy(u => new { u.OrderNo, u.Code })
+                .ToListAsync();
+
+            _sysCacheService.Set($"{CacheConst.KeyDict}{dictType.Code}", dictDataList);
+        }
+
+        return dictDataList;
     }
 
     /// <summary>
@@ -148,11 +174,17 @@ public class SysDictDataService : IDynamicApiController, ITransient
     [DisplayName("根据字典类型编码获取字典值集合")]
     public async Task<List<SysDictData>> GetDataList(string code)
     {
-        return await _sysDictDataRep.Context.Queryable<SysDictType>()
-            .LeftJoin<SysDictData>((u, a) => u.Id == a.DictTypeId)
-            .Where((u, a) => u.Code == code && u.Status == StatusEnum.Enable && a.Status == StatusEnum.Enable)
-            .OrderBy((u, a) => new { a.OrderNo, a.Code })
-            .Select((u, a) => a).ToListAsync();
+        var dictDataList = _sysCacheService.Get<List<SysDictData>>($"{CacheConst.KeyDict}{code}");
+        if (dictDataList == null)
+        {
+            dictDataList = await _sysDictDataRep.Context.Queryable<SysDictType>()
+                .LeftJoin<SysDictData>((u, a) => u.Id == a.DictTypeId)
+                .Where((u, a) => u.Code == code && u.Status == StatusEnum.Enable && a.Status == StatusEnum.Enable)
+                .OrderBy((u, a) => new { a.OrderNo, a.Code })
+                .Select((u, a) => a).ToListAsync();
+            _sysCacheService.Set($"{CacheConst.KeyDict}{code}", dictDataList);
+        }
+        return dictDataList;
     }
 
     /// <summary>
@@ -179,6 +211,9 @@ public class SysDictDataService : IDynamicApiController, ITransient
     [NonAction]
     public async Task DeleteDictData(long dictTypeId)
     {
+        var dictTypeCode = await _sysDictDataRep.AsQueryable().Where(u => u.DictTypeId == dictTypeId).Select(u => u.DictType.Code).FirstAsync();
+        _sysCacheService.Remove($"{CacheConst.KeyDict}{dictTypeCode}");
+
         await _sysDictDataRep.DeleteAsync(u => u.DictTypeId == dictTypeId);
     }
 }

+ 11 - 1
Admin.NET/Admin.NET.Core/Service/Dict/SysDictTypeService.cs

@@ -15,12 +15,15 @@ public class SysDictTypeService : IDynamicApiController, ITransient
 {
     private readonly SqlSugarRepository<SysDictType> _sysDictTypeRep;
     private readonly SysDictDataService _sysDictDataService;
+    private readonly SysCacheService _sysCacheService;
 
     public SysDictTypeService(SqlSugarRepository<SysDictType> sysDictTypeRep,
-        SysDictDataService sysDictDataService)
+        SysDictDataService sysDictDataService,
+        SysCacheService sysCacheService)
     {
         _sysDictTypeRep = sysDictTypeRep;
         _sysDictDataService = sysDictDataService;
+        _sysCacheService = sysCacheService;
     }
 
     /// <summary>
@@ -52,6 +55,7 @@ public class SysDictTypeService : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
+    [UnitOfWork]
     [AllowAnonymous]
     [DisplayName("获取字典类型-值列表")]
     public async Task<List<SysDictData>> GetDataList([FromQuery] GetDataDictTypeInput input)
@@ -84,6 +88,7 @@ public class SysDictTypeService : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
+    [UnitOfWork]
     [ApiDescriptionSettings(Name = "Update"), HttpPost]
     [DisplayName("更新字典类型")]
     public async Task UpdateDictType(UpdateDictTypeInput input)
@@ -96,6 +101,7 @@ public class SysDictTypeService : IDynamicApiController, ITransient
         if (isExist)
             throw Oops.Oh(ErrorCodeEnum.D3001);
 
+        _sysCacheService.Remove($"{CacheConst.KeyDict}{input.Code}");
         await _sysDictTypeRep.UpdateAsync(input.Adapt<SysDictType>());
     }
 
@@ -104,6 +110,7 @@ public class SysDictTypeService : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
+    [UnitOfWork]
     [ApiDescriptionSettings(Name = "Delete"), HttpPost]
     [DisplayName("删除字典类型")]
     public async Task DeleteDictType(DeleteDictTypeInput input)
@@ -133,6 +140,7 @@ public class SysDictTypeService : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
+    [UnitOfWork]
     [DisplayName("修改字典类型状态")]
     public async Task SetStatus(DictTypeInput input)
     {
@@ -143,6 +151,8 @@ public class SysDictTypeService : IDynamicApiController, ITransient
         if (!Enum.IsDefined(typeof(StatusEnum), input.Status))
             throw Oops.Oh(ErrorCodeEnum.D3005);
 
+        _sysCacheService.Remove($"{CacheConst.KeyDict}{dictType.Code}");
+
         dictType.Status = input.Status;
         await _sysDictTypeRep.UpdateAsync(dictType);
     }

+ 4 - 1
Admin.NET/Admin.NET.Core/Service/Enum/SysEnumService.cs

@@ -27,7 +27,10 @@ public class SysEnumService : IDynamicApiController, ITransient
     [DisplayName("获取所有枚举类型")]
     public List<EnumTypeOutput> GetEnumTypeList()
     {
-        var enumTypeList = App.EffectiveTypes.Where(u => u.IsEnum && _enumOptions.EntityAssemblyNames.Contains(u.Assembly.GetName().Name)).OrderBy(u => u.Name).OrderBy(u => u.FullName).ToList();
+        var enumTypeList = App.EffectiveTypes.Where(t => t.IsEnum)
+            .Where(t => _enumOptions.EntityAssemblyNames.Contains(t.Assembly.GetName().Name) || _enumOptions.EntityAssemblyNames.Any(name => t.Assembly.GetName().Name.Contains(name)))
+            .OrderBy(u => u.Name).OrderBy(u => u.FullName)
+            .ToList();
 
         var result = new List<EnumTypeOutput>();
         foreach (var item in enumTypeList)

+ 16 - 30
Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs

@@ -21,7 +21,7 @@ public class SysFileService : IDynamicApiController, ITransient
     private readonly OSSProviderOptions _OSSProviderOptions;
     private readonly UploadOptions _uploadOptions;
     private readonly IOSSService _OSSService;
-    private readonly string _imageType = ".jpg.png.bmp.gif.tif";
+    private readonly string _imageType = ".jpeg.jpg.png.bmp.gif.tif";
 
     public SysFileService(UserManager userManager,
         SqlSugarRepository<SysFile> sysFileRep,
@@ -65,41 +65,27 @@ public class SysFileService : IDynamicApiController, ITransient
     }
 
     /// <summary>
-    /// 上传文件Base64
+    /// 上传文件Base64 🔖
     /// </summary>
-    /// <param name="strBase64"></param>
-    /// <param name="fileName"></param>
-    /// <param name="contentType"></param>
-    /// <param name="path"></param>
-    /// <param name="fileType"></param>
+    /// <param name="input"></param>
     /// <returns></returns>
-    private async Task<SysFile> UploadFileFromBase64(string strBase64, string fileName, string contentType, string? path, string? fileType)
+    [DisplayName("上传文件Base64")]
+    public async Task<SysFile> UploadFileFromBase64(UploadFileFromBase64Input input)
     {
-        byte[] fileData = Convert.FromBase64String(strBase64);
+        byte[] fileData = Convert.FromBase64String(input.FileDataBase64);
         var ms = new MemoryStream();
         ms.Write(fileData);
         ms.Seek(0, SeekOrigin.Begin);
-        if (string.IsNullOrEmpty(fileName))
-            fileName = $"{YitIdHelper.NextId()}.jpg";
-        if (string.IsNullOrEmpty(contentType))
-            contentType = "image/jpg";
-        IFormFile formFile = new FormFile(ms, 0, fileData.Length, "file", fileName)
+        if (string.IsNullOrEmpty(input.FileName))
+            input.FileName = $"{YitIdHelper.NextId()}.jpg";
+        if (string.IsNullOrEmpty(input.ContentType))
+            input.ContentType = "image/jpg";
+        IFormFile formFile = new FormFile(ms, 0, fileData.Length, "file", input.FileName)
         {
             Headers = new HeaderDictionary(),
-            ContentType = contentType
+            ContentType = input.ContentType
         };
-        return await UploadFile(new FileUploadInput { File = formFile, Path = path, FileType = fileType });
-    }
-
-    /// <summary>
-    /// 上传文件Base64 🔖
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [DisplayName("上传文件Base64")]
-    public async Task<SysFile> UploadFileFromBase64(UploadFileFromBase64Input input)
-    {
-        return await UploadFileFromBase64(input.FileDataBase64, input.FileName, input.ContentType, input.Path, input.FileType);
+        return await UploadFile(new FileUploadInput { File = formFile, Path = input.Path, FileType = input.FileType });
     }
 
     /// <summary>
@@ -335,10 +321,10 @@ public class SysFileService : IDynamicApiController, ITransient
 
         // 获取文件后缀
         var suffix = Path.GetExtension(file.FileName).ToLower(); // 后缀
-        if (string.IsNullOrWhiteSpace(suffix))
+        if (!string.IsNullOrWhiteSpace(suffix))
         {
-            var contentTypeProvider = FS.GetFileExtensionContentTypeProvider();
-            suffix = contentTypeProvider.Mappings.FirstOrDefault(u => u.Value == file.ContentType).Key;
+            //var contentTypeProvider = FS.GetFileExtensionContentTypeProvider();
+            //suffix = contentTypeProvider.Mappings.FirstOrDefault(u => u.Value == file.ContentType).Key;
             // 修改 image/jpeg 类型返回的 .jpeg、jpe 后缀
             if (suffix == ".jpeg" || suffix == ".jpe")
                 suffix = ".jpg";

+ 11 - 8
Admin.NET/Admin.NET.Core/Service/Job/DbJobPersistence.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -27,7 +27,7 @@ public class DbJobPersistence : IJobPersistence
     public async Task<IEnumerable<SchedulerBuilder>> PreloadAsync(CancellationToken stoppingToken)
     {
         using var scope = _serviceScopeFactory.CreateScope();
-        var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>();
+        var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
         var dynamicJobCompiler = scope.ServiceProvider.GetRequiredService<DynamicJobCompiler>();
 
         // 获取所有定义的作业
@@ -128,11 +128,12 @@ public class DbJobPersistence : IJobPersistence
     /// 作业计划Scheduler的JobDetail变化时
     /// </summary>
     /// <param name="context"></param>
+    /// <returns></returns>
     public async Task OnChangedAsync(PersistenceContext context)
     {
         using (var scope = _serviceScopeFactory.CreateScope())
         {
-            var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>();
+            var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
 
             var jobDetail = context.JobDetail.Adapt<SysJobDetail>();
             switch (context.Behavior)
@@ -156,11 +157,12 @@ public class DbJobPersistence : IJobPersistence
     /// 作业计划Scheduler的触发器Trigger变化时
     /// </summary>
     /// <param name="context"></param>
+    /// <returns></returns>
     public async Task OnTriggerChangedAsync(PersistenceTriggerContext context)
     {
         using (var scope = _serviceScopeFactory.CreateScope())
         {
-            var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>();
+            var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
 
             var jobTrigger = context.Trigger.Adapt<SysJobTrigger>();
             switch (context.Behavior)
@@ -183,14 +185,15 @@ public class DbJobPersistence : IJobPersistence
     /// <summary>
     /// 作业触发器运行记录
     /// </summary>
-    /// <param name="timeline"></param>
-    public async Task OnExecutionRecordAsync(TriggerTimeline timeline)
+    /// <param name="context"></param>
+    /// <returns></returns>
+    public async Task OnExecutionRecordAsync(PersistenceExecutionRecordContext context)
     {
         using (var scope = _serviceScopeFactory.CreateScope())
         {
-            var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>();
+            var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
 
-            var jobTriggerRecord = timeline.Adapt<SysJobTriggerRecord>();
+            var jobTriggerRecord = context.Timeline.Adapt<SysJobTriggerRecord>();
             await db.Insertable(jobTriggerRecord).ExecuteCommandAsync();
         }
     }

+ 106 - 106
Admin.NET/Admin.NET.Core/Service/Job/JobClusterServer.cs

@@ -1,107 +1,107 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Core.Service;
-
-/// <summary>
-/// 作业集群控制
-/// </summary>
-public class JobClusterServer : IJobClusterServer
-{
-    private readonly Random rd = new(DateTime.Now.Millisecond);
-
-    public JobClusterServer()
-    {
-    }
-
-    /// <summary>
-    /// 当前作业调度器启动通知
-    /// </summary>
-    /// <param name="context">作业集群服务上下文</param>
-    public async void Start(JobClusterContext context)
-    {
-        var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
-        // 在作业集群表中,如果 clusterId 不存在,则新增一条(否则更新一条),并设置 status 为 ClusterStatus.Waiting
-        if (await _sysJobClusterRep.IsAnyAsync(u => u.ClusterId == context.ClusterId))
-        {
-            await _sysJobClusterRep.AsUpdateable().SetColumns(u => u.Status == ClusterStatus.Waiting).Where(u => u.ClusterId == context.ClusterId).ExecuteCommandAsync();
-        }
-        else
-        {
-            await _sysJobClusterRep.AsInsertable(new SysJobCluster { ClusterId = context.ClusterId, Status = ClusterStatus.Waiting }).ExecuteCommandAsync();
-        }
-    }
-
-    /// <summary>
-    /// 等待被唤醒
-    /// </summary>
-    /// <param name="context">作业集群服务上下文</param>
-    /// <returns><see cref="Task"/></returns>
-    public async Task WaitingForAsync(JobClusterContext context)
-    {
-        var clusterId = context.ClusterId;
-
-        while (true)
-        {
-            // 控制集群心跳频率(放在头部为了防止 IsAnyAsync continue 没sleep占用大量IO和CPU)
-            await Task.Delay(3000 + rd.Next(500, 1000)); // 错开集群同时启动
-
-            try
-            {
-                ICache _cache = App.GetRequiredService<ICache>();
-                // 使用分布式锁
-                using (_cache.AcquireLock("lock:JobClusterServer:WaitingForAsync", 1000))
-                {
-                    var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
-                    // 在这里查询数据库,根据以下两种情况处理
-                    // 1) 如果作业集群表已有 status 为 ClusterStatus.Working 则继续循环
-                    // 2) 如果作业集群表中还没有其他服务或只有自己,则插入一条集群服务或调用 await WorkNowAsync(clusterId); 之后 return;
-                    // 3) 如果作业集群表中没有 status 为 ClusterStatus.Working 的,调用 await WorkNowAsync(clusterId); 之后 return;
-                    if (await _sysJobClusterRep.IsAnyAsync(u => u.Status == ClusterStatus.Working))
-                        continue;
-
-                    await WorkNowAsync(clusterId);
-                    return;
-                }
-            }
-            catch { }
-        }
-    }
-
-    /// <summary>
-    /// 当前作业调度器停止通知
-    /// </summary>
-    /// <param name="context">作业集群服务上下文</param>
-    public async void Stop(JobClusterContext context)
-    {
-        var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
-        // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed
-        await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId);
-    }
-
-    /// <summary>
-    /// 当前作业调度器宕机
-    /// </summary>
-    /// <param name="context">作业集群服务上下文</param>
-    public async void Crash(JobClusterContext context)
-    {
-        var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
-        // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed
-        await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId);
-    }
-
-    /// <summary>
-    /// 指示集群可以工作
-    /// </summary>
-    /// <param name="clusterId">集群 Id</param>
-    /// <returns></returns>
-    private static async Task WorkNowAsync(string clusterId)
-    {
-        var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
-        // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Working
-        await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Working }, u => u.ClusterId == clusterId);
-    }
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 作业集群控制
+/// </summary>
+public class JobClusterServer : IJobClusterServer
+{
+    private readonly Random rd = new(DateTime.Now.Millisecond);
+
+    public JobClusterServer()
+    {
+    }
+
+    /// <summary>
+    /// 当前作业调度器启动通知
+    /// </summary>
+    /// <param name="context">作业集群服务上下文</param>
+    public async void Start(JobClusterContext context)
+    {
+        var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
+        // 在作业集群表中,如果 clusterId 不存在,则新增一条(否则更新一条),并设置 status 为 ClusterStatus.Waiting
+        if (await _sysJobClusterRep.IsAnyAsync(u => u.ClusterId == context.ClusterId))
+        {
+            await _sysJobClusterRep.AsUpdateable().SetColumns(u => u.Status == ClusterStatus.Waiting).Where(u => u.ClusterId == context.ClusterId).ExecuteCommandAsync();
+        }
+        else
+        {
+            await _sysJobClusterRep.AsInsertable(new SysJobCluster { ClusterId = context.ClusterId, Status = ClusterStatus.Waiting }).ExecuteCommandAsync();
+        }
+    }
+
+    /// <summary>
+    /// 等待被唤醒
+    /// </summary>
+    /// <param name="context">作业集群服务上下文</param>
+    /// <returns><see cref="Task"/></returns>
+    public async Task WaitingForAsync(JobClusterContext context)
+    {
+        var clusterId = context.ClusterId;
+
+        while (true)
+        {
+            // 控制集群心跳频率(放在头部为了防止 IsAnyAsync continue 没sleep占用大量IO和CPU)
+            await Task.Delay(3000 + rd.Next(500, 1000)); // 错开集群同时启动
+
+            try
+            {
+                ICache _cache = App.GetRequiredService<ICacheProvider>().Cache;
+                // 使用分布式锁
+                using (_cache.AcquireLock("lock:JobClusterServer:WaitingForAsync", 1000))
+                {
+                    var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
+                    // 在这里查询数据库,根据以下两种情况处理
+                    // 1) 如果作业集群表已有 status 为 ClusterStatus.Working 则继续循环
+                    // 2) 如果作业集群表中还没有其他服务或只有自己,则插入一条集群服务或调用 await WorkNowAsync(clusterId); 之后 return;
+                    // 3) 如果作业集群表中没有 status 为 ClusterStatus.Working 的,调用 await WorkNowAsync(clusterId); 之后 return;
+                    if (await _sysJobClusterRep.IsAnyAsync(u => u.Status == ClusterStatus.Working))
+                        continue;
+
+                    await WorkNowAsync(clusterId);
+                    return;
+                }
+            }
+            catch { }
+        }
+    }
+
+    /// <summary>
+    /// 当前作业调度器停止通知
+    /// </summary>
+    /// <param name="context">作业集群服务上下文</param>
+    public async void Stop(JobClusterContext context)
+    {
+        var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
+        // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed
+        await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId);
+    }
+
+    /// <summary>
+    /// 当前作业调度器宕机
+    /// </summary>
+    /// <param name="context">作业集群服务上下文</param>
+    public async void Crash(JobClusterContext context)
+    {
+        var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
+        // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed
+        await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId);
+    }
+
+    /// <summary>
+    /// 指示集群可以工作
+    /// </summary>
+    /// <param name="clusterId">集群 Id</param>
+    /// <returns></returns>
+    private static async Task WorkNowAsync(string clusterId)
+    {
+        var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
+        // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Working
+        await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Working }, u => u.ClusterId == clusterId);
+    }
 }

+ 47 - 6
Admin.NET/Admin.NET.Core/Service/Message/SysSmsService.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -8,6 +8,7 @@ using AlibabaCloud.SDK.Dysmsapi20170525.Models;
 using TencentCloud.Common;
 using TencentCloud.Common.Profile;
 using TencentCloud.Sms.V20190711;
+using static SKIT.FlurlHttpClient.Wechat.Api.Models.ComponentTCBBatchCreateContainerServiceVersionRequest.Types;
 
 namespace Admin.NET.Core.Service;
 
@@ -65,15 +66,16 @@ public class SysSmsService : IDynamicApiController, ITransient
         });
 
         var client = CreateAliyunClient();
+        var template = _smsOptions.Aliyun.GetTemplate();
         var sendSmsRequest = new SendSmsRequest
         {
             PhoneNumbers = phoneNumber, // 待发送手机号, 多个以逗号分隔
-            SignName = _smsOptions.Aliyun.SignName, // 短信签名
-            TemplateCode = _smsOptions.Aliyun.TemplateCode, // 短信模板
+            SignName = template.SignName, // 短信签名
+            TemplateCode = template.TemplateCode, // 短信模板
             TemplateParam = templateParam.ToString(), // 模板中的变量替换JSON串
             OutId = YitIdHelper.NextId().ToString()
         };
-        var sendSmsResponse = client.SendSms(sendSmsRequest);
+        var sendSmsResponse = await client.SendSmsAsync(sendSmsRequest);
         if (sendSmsResponse.Body.Code == "OK" && sendSmsResponse.Body.Message == "OK")
         {
             // var bizId = sendSmsResponse.Body.BizId;
@@ -87,6 +89,44 @@ public class SysSmsService : IDynamicApiController, ITransient
         await Task.CompletedTask;
     }
 
+    /// <summary>
+    /// 发送短信模板
+    /// </summary>
+    /// <param name="phoneNumber"></param>
+    /// <param name="templateParam"></param>
+    /// <returns></returns>
+    [AllowAnonymous]
+    [DisplayName("发送短信模板")]
+    public async Task AliyunSendSmsTemplate([Required] string phoneNumber, [Required] dynamic templateParam)
+    {
+        if (!phoneNumber.TryValidate(ValidationTypes.PhoneNumber).IsValid)
+            throw Oops.Oh("请正确填写手机号码");
+
+        if (string.IsNullOrWhiteSpace(templateParam.ToString()))
+            throw Oops.Oh("短信内容不能为空");
+
+        var client = CreateAliyunClient();
+        var template = _smsOptions.Aliyun.GetTemplate();
+        var sendSmsRequest = new SendSmsRequest
+        {
+            PhoneNumbers = phoneNumber, // 待发送手机号, 多个以逗号分隔
+            SignName = template.SignName, // 短信签名
+            TemplateCode = template.TemplateCode, // 短信模板
+            TemplateParam = templateParam.ToString(), // 模板中的变量替换JSON串
+            OutId = YitIdHelper.NextId().ToString()
+        };
+        var sendSmsResponse = await client.SendSmsAsync(sendSmsRequest);
+        if (sendSmsResponse.Body.Code == "OK" && sendSmsResponse.Body.Message == "OK")
+        {
+        }
+        else
+        {
+            throw Oops.Oh($"短信发送失败:{sendSmsResponse.Body.Code}-{sendSmsResponse.Body.Message}");
+        }
+
+        await Task.CompletedTask;
+    }
+
     /// <summary>
     /// 腾讯云发送短信 📨
     /// </summary>
@@ -105,13 +145,14 @@ public class SysSmsService : IDynamicApiController, ITransient
 
         // 实例化要请求产品的client对象,clientProfile是可选的
         var client = new SmsClient(CreateTencentClient(), "ap-guangzhou", new ClientProfile() { HttpProfile = new HttpProfile() { Endpoint = ("sms.tencentcloudapi.com") } });
+        var template = _smsOptions.Tencentyun.GetTemplate();
         // 实例化一个请求对象,每个接口都会对应一个request对象
         var req = new TencentCloud.Sms.V20190711.Models.SendSmsRequest
         {
             PhoneNumberSet = new string[] { "+86" + phoneNumber.Trim(',') },
             SmsSdkAppid = _smsOptions.Tencentyun.SdkAppId,
-            Sign = _smsOptions.Tencentyun.SignName,
-            TemplateID = _smsOptions.Tencentyun.TemplateCode,
+            Sign = template.SignName,
+            TemplateID = template.TemplateCode,
             TemplateParamSet = new string[] { verifyCode.ToString() }
         };
 

+ 9 - 16
Admin.NET/Admin.NET.Core/Service/OnlineUser/SysOnlineUserService.cs

@@ -16,22 +16,15 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
 {
     private readonly SysConfigService _sysConfigService;
     private readonly IHubContext<OnlineUserHub, IOnlineUserHub> _onlineUserHubContext;
-    private SqlSugarRepository<SysOnlineUser> sysOnlineUerRep = null;
+    private readonly SqlSugarRepository<SysOnlineUser> _sysOnlineUerRep;
 
     public SysOnlineUserService(SysConfigService sysConfigService,
-        IHubContext<OnlineUserHub, IOnlineUserHub> onlineUserHubContext)
+        IHubContext<OnlineUserHub, IOnlineUserHub> onlineUserHubContext,
+        SqlSugarRepository<SysOnlineUser> sysOnlineUerRep)
     {
         _sysConfigService = sysConfigService;
         _onlineUserHubContext = onlineUserHubContext;
-    }
-
-    public SqlSugarRepository<SysOnlineUser> SysOnlineUerRep
-    {
-        get
-        {
-            sysOnlineUerRep ??= App.GetRequiredService<SqlSugarRepository<SysOnlineUser>>();
-            return sysOnlineUerRep;
-        }
+        _sysOnlineUerRep = sysOnlineUerRep;
     }
 
     /// <summary>
@@ -41,7 +34,7 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
     [DisplayName("获取在线用户分页列表")]
     public async Task<SqlSugarPagedList<SysOnlineUser>> Page(PageOnlineUserInput input)
     {
-        return await SysOnlineUerRep.AsQueryable()
+        return await _sysOnlineUerRep.AsQueryable()
             .WhereIF(!string.IsNullOrWhiteSpace(input.UserName), u => u.UserName.Contains(input.UserName))
             .WhereIF(!string.IsNullOrWhiteSpace(input.RealName), u => u.RealName.Contains(input.RealName))
             .ToPagedListAsync(input.Page, input.PageSize);
@@ -57,7 +50,7 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
     public async Task ForceOffline(SysOnlineUser user)
     {
         await _onlineUserHubContext.Clients.Client(user.ConnectionId).ForceOffline("强制下线");
-        await SysOnlineUerRep.DeleteAsync(user);
+        await _sysOnlineUerRep.DeleteAsync(user);
     }
 
     /// <summary>
@@ -69,7 +62,7 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
     [NonAction]
     public async Task PublicNotice(SysNotice notice, List<long> userIds)
     {
-        var userList = await SysOnlineUerRep.GetListAsync(u => userIds.Contains(u.UserId));
+        var userList = await _sysOnlineUerRep.GetListAsync(u => userIds.Contains(u.UserId));
         if (userList.Count == 0) return;
 
         foreach (var item in userList)
@@ -87,7 +80,7 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
     {
         if (await _sysConfigService.GetConfigValue<bool>(CommonConst.SysSingleLogin))
         {
-            var users = await SysOnlineUerRep.GetListAsync(u => u.UserId == userId);
+            var users = await _sysOnlineUerRep.GetListAsync(u => u.UserId == userId);
             foreach (var user in users)
             {
                 await ForceOffline(user);
@@ -103,7 +96,7 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
     [NonAction]
     public async Task ForceOffline(long userId)
     {
-        var users = await SysOnlineUerRep.GetListAsync(u => u.UserId == userId);
+        var users = await _sysOnlineUerRep.GetListAsync(u => u.UserId == userId);
         foreach (var user in users)
         {
             await ForceOffline(user);

+ 6 - 0
Admin.NET/Admin.NET.Core/Service/Org/Dto/OrgInput.cs

@@ -31,6 +31,12 @@ public class AddOrgInput : SysOrg
     /// </summary>
     [Required(ErrorMessage = "机构名称不能为空")]
     public override string Name { get; set; }
+
+    /// <summary>
+    /// 机构类型
+    /// </summary>
+    [Dict("org_type", ErrorMessage = "机构类型不能合法", AllowNullValue = true, AllowEmptyStrings = true)]
+    public override string? Type { get; set; }
 }
 
 public class UpdateOrgInput : AddOrgInput

+ 25 - 32
Admin.NET/Admin.NET.Core/Service/Org/SysOrgService.cs

@@ -17,28 +17,21 @@ public class SysOrgService : IDynamicApiController, ITransient
     private readonly SysUserExtOrgService _sysUserExtOrgService;
     private readonly SysUserRoleService _sysUserRoleService;
     private readonly SysRoleOrgService _sysRoleOrgService;
-    private SqlSugarRepository<SysOrg> sysOrgRep = null;
+    private readonly SqlSugarRepository<SysOrg> _sysOrgRep;
 
     public SysOrgService(UserManager userManager,
         SysCacheService sysCacheService,
         SysUserExtOrgService sysUserExtOrgService,
         SysUserRoleService sysUserRoleService,
-        SysRoleOrgService sysRoleOrgService)
+        SysRoleOrgService sysRoleOrgService,
+        SqlSugarRepository<SysOrg> sysOrgRep)
     {
         _userManager = userManager;
         _sysCacheService = sysCacheService;
         _sysUserExtOrgService = sysUserExtOrgService;
         _sysUserRoleService = sysUserRoleService;
         _sysRoleOrgService = sysRoleOrgService;
-    }
-
-    public SqlSugarRepository<SysOrg> SysOrgRep
-    {
-        get
-        {
-            sysOrgRep ??= App.GetRequiredService<SqlSugarRepository<SysOrg>>();
-            return sysOrgRep;
-        }
+        _sysOrgRep = sysOrgRep;
     }
 
     /// <summary>
@@ -51,7 +44,7 @@ public class SysOrgService : IDynamicApiController, ITransient
         // 获取拥有的机构Id集合
         var userOrgIdList = await GetUserOrgIdList();
 
-        var iSugarQueryable = SysOrgRep.AsQueryable().OrderBy(u => u.OrderNo);
+        var iSugarQueryable = _sysOrgRep.AsQueryable().OrderBy(u => u.OrderNo);
 
         // 带条件筛选时返回列表数据
         if (!string.IsNullOrWhiteSpace(input.Name) || !string.IsNullOrWhiteSpace(input.Code) || !string.IsNullOrWhiteSpace(input.Type))
@@ -75,7 +68,7 @@ public class SysOrgService : IDynamicApiController, ITransient
             HandlerOrgTree(orgTree, userOrgIdList);
         }
 
-        var sysOrg = await SysOrgRep.GetSingleAsync(u => u.Id == input.Id);
+        var sysOrg = await _sysOrgRep.GetSingleAsync(u => u.Id == input.Id);
         if (sysOrg != null)
         {
             sysOrg.Children = orgTree;
@@ -111,7 +104,7 @@ public class SysOrgService : IDynamicApiController, ITransient
         if (!_userManager.SuperAdmin && input.Pid == 0)
             throw Oops.Oh(ErrorCodeEnum.D2009);
 
-        if (await SysOrgRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code))
+        if (await _sysOrgRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code))
             throw Oops.Oh(ErrorCodeEnum.D2002);
 
         if (!_userManager.SuperAdmin && input.Pid != 0)
@@ -130,12 +123,12 @@ public class SysOrgService : IDynamicApiController, ITransient
         }
         else
         {
-            var pOrg = await SysOrgRep.GetFirstAsync(u => u.Id == input.Pid);
+            var pOrg = await _sysOrgRep.GetFirstAsync(u => u.Id == input.Pid);
             if (pOrg != null)
                 DeleteAllUserOrgCache(pOrg.Id, pOrg.Pid);
         }
 
-        var newOrg = await SysOrgRep.AsInsertable(input.Adapt<SysOrg>()).ExecuteReturnEntityAsync();
+        var newOrg = await _sysOrgRep.AsInsertable(input.Adapt<SysOrg>()).ExecuteReturnEntityAsync();
         return newOrg.Id;
     }
 
@@ -148,8 +141,8 @@ public class SysOrgService : IDynamicApiController, ITransient
     public async Task BatchAddOrgs(List<SysOrg> orgs)
     {
         DeleteAllUserOrgCache(0, 0);
-        await SysOrgRep.AsDeleteable().ExecuteCommandAsync();
-        await SysOrgRep.AsInsertable(orgs).ExecuteCommandAsync();
+        await _sysOrgRep.AsDeleteable().ExecuteCommandAsync();
+        await _sysOrgRep.AsInsertable(orgs).ExecuteCommandAsync();
     }
 
     /// <summary>
@@ -171,7 +164,7 @@ public class SysOrgService : IDynamicApiController, ITransient
             //_ = pOrg ?? throw Oops.Oh(ErrorCodeEnum.D2000);
 
             // 若父机构发生变化则清空用户机构缓存
-            var sysOrg = await SysOrgRep.GetFirstAsync(u => u.Id == input.Id);
+            var sysOrg = await _sysOrgRep.GetFirstAsync(u => u.Id == input.Id);
             if (sysOrg != null && sysOrg.Pid != input.Pid)
             {
                 // 删除与此机构、新父机构有关的用户机构缓存
@@ -181,7 +174,7 @@ public class SysOrgService : IDynamicApiController, ITransient
         if (input.Id == input.Pid)
             throw Oops.Oh(ErrorCodeEnum.D2001);
 
-        if (await SysOrgRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code && u.Id != input.Id))
+        if (await _sysOrgRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code && u.Id != input.Id))
             throw Oops.Oh(ErrorCodeEnum.D2002);
 
         // 父Id不能为自己的子节点
@@ -197,7 +190,7 @@ public class SysOrgService : IDynamicApiController, ITransient
                 throw Oops.Oh(ErrorCodeEnum.D2003);
         }
 
-        await SysOrgRep.AsUpdateable(input.Adapt<SysOrg>()).IgnoreColumns(true).ExecuteCommandAsync();
+        await _sysOrgRep.AsUpdateable(input.Adapt<SysOrg>()).IgnoreColumns(true).ExecuteCommandAsync();
     }
 
     /// <summary>
@@ -210,7 +203,7 @@ public class SysOrgService : IDynamicApiController, ITransient
     [DisplayName("删除机构")]
     public async Task DeleteOrg(DeleteOrgInput input)
     {
-        var sysOrg = await SysOrgRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+        var sysOrg = await _sysOrgRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
 
         // 是否有权限操作此机构
         if (!_userManager.SuperAdmin)
@@ -221,13 +214,13 @@ public class SysOrgService : IDynamicApiController, ITransient
         }
 
         // 若机构为租户默认机构禁止删除
-        var isTenantOrg = await SysOrgRep.ChangeRepository<SqlSugarRepository<SysTenant>>()
+        var isTenantOrg = await _sysOrgRep.ChangeRepository<SqlSugarRepository<SysTenant>>()
             .IsAnyAsync(u => u.OrgId == input.Id);
         if (isTenantOrg)
             throw Oops.Oh(ErrorCodeEnum.D2008);
 
         // 若机构有用户则禁止删除
-        var orgHasEmp = await SysOrgRep.ChangeRepository<SqlSugarRepository<SysUser>>()
+        var orgHasEmp = await _sysOrgRep.ChangeRepository<SqlSugarRepository<SysUser>>()
             .IsAnyAsync(u => u.OrgId == input.Id);
         if (orgHasEmp)
             throw Oops.Oh(ErrorCodeEnum.D2004);
@@ -238,11 +231,11 @@ public class SysOrgService : IDynamicApiController, ITransient
             throw Oops.Oh(ErrorCodeEnum.D2005);
 
         // 若子机构有用户则禁止删除
-        var childOrgTreeList = await SysOrgRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true);
+        var childOrgTreeList = await _sysOrgRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true);
         var childOrgIdList = childOrgTreeList.Select(u => u.Id).ToList();
 
         // 若子机构有用户则禁止删除
-        var cOrgHasEmp = await SysOrgRep.ChangeRepository<SqlSugarRepository<SysUser>>()
+        var cOrgHasEmp = await _sysOrgRep.ChangeRepository<SqlSugarRepository<SysUser>>()
             .IsAnyAsync(u => childOrgIdList.Contains(u.OrgId));
         if (cOrgHasEmp)
             throw Oops.Oh(ErrorCodeEnum.D2007);
@@ -251,7 +244,7 @@ public class SysOrgService : IDynamicApiController, ITransient
         DeleteAllUserOrgCache(sysOrg.Id, sysOrg.Pid);
 
         // 级联删除机构子节点
-        await SysOrgRep.DeleteAsync(u => childOrgIdList.Contains(u.Id));
+        await _sysOrgRep.DeleteAsync(u => childOrgIdList.Contains(u.Id));
 
         // 级联删除角色机构数据
         await _sysRoleOrgService.DeleteRoleOrgByOrgIdList(childOrgIdList);
@@ -276,14 +269,14 @@ public class SysOrgService : IDynamicApiController, ITransient
                 var userId = long.Parse(userOrgKey.Substring(CacheConst.KeyUserOrg));
                 if (userOrgs != null && (userOrgs.Contains(orgId) || userOrgs.Contains(orgPid)))
                 {
-                    SqlSugarFilter.DeleteUserOrgCache(userId, SysOrgRep.Context.CurrentConnectionConfig.ConfigId.ToString());
+                    SqlSugarFilter.DeleteUserOrgCache(userId, _sysOrgRep.Context.CurrentConnectionConfig.ConfigId.ToString());
                 }
                 if (orgPid == 0)
                 {
                     var dataScope = _sysCacheService.Get<int>($"{CacheConst.KeyRoleMaxDataScope}{userId}");
                     if (dataScope == (int)DataScopeEnum.All)
                     {
-                        SqlSugarFilter.DeleteUserOrgCache(userId, SysOrgRep.Context.CurrentConnectionConfig.ConfigId.ToString());
+                        SqlSugarFilter.DeleteUserOrgCache(userId, _sysOrgRep.Context.CurrentConnectionConfig.ConfigId.ToString());
                     }
                 }
             }
@@ -313,7 +306,7 @@ public class SysOrgService : IDynamicApiController, ITransient
         if (orgIdList == null || orgIdList.Count < 1)
         {
             // 本人创建机构集合
-            var orgList0 = await SysOrgRep.AsQueryable().Where(u => u.CreateUserId == userId).Select(u => u.Id).ToListAsync();
+            var orgList0 = await _sysOrgRep.AsQueryable().Where(u => u.CreateUserId == userId).Select(u => u.Id).ToListAsync();
             // 扩展机构集合
             var orgList1 = await _sysUserExtOrgService.GetUserExtOrgList(userId);
             // 角色机构集合
@@ -403,7 +396,7 @@ public class SysOrgService : IDynamicApiController, ITransient
         // 若数据范围是全部,则获取所有机构Id集合
         if (dataScope == (int)DataScopeEnum.All)
         {
-            orgIdList = await SysOrgRep.AsQueryable().Select(u => u.Id).ToListAsync();
+            orgIdList = await _sysOrgRep.AsQueryable().Select(u => u.Id).ToListAsync();
         }
         // 若数据范围是本部门及以下,则获取本节点和子节点集合
         else if (dataScope == (int)DataScopeEnum.DeptChild)
@@ -426,7 +419,7 @@ public class SysOrgService : IDynamicApiController, ITransient
     [NonAction]
     public async Task<List<long>> GetChildIdListWithSelfById(long pid)
     {
-        var orgTreeList = await SysOrgRep.AsQueryable().ToChildListAsync(u => u.Pid, pid, true);
+        var orgTreeList = await _sysOrgRep.AsQueryable().ToChildListAsync(u => u.Pid, pid, true);
         return orgTreeList.Select(u => u.Id).ToList();
     }
 }

+ 15 - 19
Admin.NET/Admin.NET.Core/Service/Role/SysRoleOrgService.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -11,19 +11,11 @@ namespace Admin.NET.Core.Service;
 /// </summary>
 public class SysRoleOrgService : ITransient
 {
-    private SqlSugarRepository<SysRoleOrg> sysRoleOrgRep = null;
+    private readonly SqlSugarRepository<SysRoleOrg> _sysRoleOrgRep;
 
-    public SysRoleOrgService()
+    public SysRoleOrgService(SqlSugarRepository<SysRoleOrg> sysRoleOrgRep)
     {
-    }
-
-    public SqlSugarRepository<SysRoleOrg> SysRoleOrgRep
-    {
-        get
-        {
-            sysRoleOrgRep ??= App.GetRequiredService<SqlSugarRepository<SysRoleOrg>>();
-            return sysRoleOrgRep;
-        }
+        _sysRoleOrgRep = sysRoleOrgRep;
     }
 
     /// <summary>
@@ -33,7 +25,7 @@ public class SysRoleOrgService : ITransient
     /// <returns></returns>
     public async Task GrantRoleOrg(RoleOrgInput input)
     {
-        await SysRoleOrgRep.DeleteAsync(u => u.RoleId == input.Id);
+        await _sysRoleOrgRep.DeleteAsync(u => u.RoleId == input.Id);
         if (input.DataScope == (int)DataScopeEnum.Define)
         {
             var roleOrgs = input.OrgIdList.Select(u => new SysRoleOrg
@@ -41,7 +33,7 @@ public class SysRoleOrgService : ITransient
                 RoleId = input.Id,
                 OrgId = u
             }).ToList();
-            await SysRoleOrgRep.InsertRangeAsync(roleOrgs);
+            await _sysRoleOrgRep.InsertRangeAsync(roleOrgs);
         }
     }
 
@@ -52,9 +44,13 @@ public class SysRoleOrgService : ITransient
     /// <returns></returns>
     public async Task<List<long>> GetRoleOrgIdList(List<long> roleIdList)
     {
-        return await SysRoleOrgRep.AsQueryable()
-            .Where(u => roleIdList.Contains(u.RoleId))
-            .Select(u => u.OrgId).ToListAsync();
+        if (roleIdList?.Count > 0)
+        {
+            return await _sysRoleOrgRep.AsQueryable()
+                .Where(u => roleIdList.Contains(u.RoleId))
+                .Select(u => u.OrgId).ToListAsync();
+        }
+        else return new List<long>();
     }
 
     /// <summary>
@@ -64,7 +60,7 @@ public class SysRoleOrgService : ITransient
     /// <returns></returns>
     public async Task DeleteRoleOrgByOrgIdList(List<long> orgIdList)
     {
-        await SysRoleOrgRep.DeleteAsync(u => orgIdList.Contains(u.OrgId));
+        await _sysRoleOrgRep.DeleteAsync(u => orgIdList.Contains(u.OrgId));
     }
 
     /// <summary>
@@ -74,6 +70,6 @@ public class SysRoleOrgService : ITransient
     /// <returns></returns>
     public async Task DeleteRoleOrgByRoleId(long roleId)
     {
-        await SysRoleOrgRep.DeleteAsync(u => u.RoleId == roleId);
+        await _sysRoleOrgRep.DeleteAsync(u => u.RoleId == roleId);
     }
 }

+ 1 - 0
Admin.NET/Admin.NET.Core/Service/User/Dto/UserInput.cs

@@ -14,6 +14,7 @@ public class UserInput : BaseIdInput
     /// <summary>
     /// 状态
     /// </summary>
+    [Dict("StatusEnum")]
     public StatusEnum Status { get; set; }
 }
 

+ 10 - 18
Admin.NET/Admin.NET.Core/Service/User/SysUserExtOrgService.cs

@@ -11,19 +11,11 @@ namespace Admin.NET.Core.Service;
 /// </summary>
 public class SysUserExtOrgService : ITransient
 {
-    private SqlSugarRepository<SysUserExtOrg> sysUserExtOrgRep = null;
+    private readonly SqlSugarRepository<SysUserExtOrg> _sysUserExtOrgRep;
 
-    public SysUserExtOrgService()
+    public SysUserExtOrgService(SqlSugarRepository<SysUserExtOrg> sysUserExtOrgRep)
     {
-    }
-
-    public SqlSugarRepository<SysUserExtOrg> SysUserExtOrgRep
-    {
-        get
-        {
-            sysUserExtOrgRep ??= App.GetRequiredService<SqlSugarRepository<SysUserExtOrg>>();
-            return sysUserExtOrgRep;
-        }
+        _sysUserExtOrgRep = sysUserExtOrgRep;
     }
 
     /// <summary>
@@ -33,7 +25,7 @@ public class SysUserExtOrgService : ITransient
     /// <returns></returns>
     public async Task<List<SysUserExtOrg>> GetUserExtOrgList(long userId)
     {
-        return await SysUserExtOrgRep.GetListAsync(u => u.UserId == userId);
+        return await _sysUserExtOrgRep.GetListAsync(u => u.UserId == userId);
     }
 
     /// <summary>
@@ -44,14 +36,14 @@ public class SysUserExtOrgService : ITransient
     /// <returns></returns>
     public async Task UpdateUserExtOrg(long userId, List<SysUserExtOrg> extOrgList)
     {
-        await SysUserExtOrgRep.DeleteAsync(u => u.UserId == userId);
+        await _sysUserExtOrgRep.DeleteAsync(u => u.UserId == userId);
 
         if (extOrgList == null || extOrgList.Count < 1) return;
         extOrgList.ForEach(u =>
         {
             u.UserId = userId;
         });
-        await SysUserExtOrgRep.InsertRangeAsync(extOrgList);
+        await _sysUserExtOrgRep.InsertRangeAsync(extOrgList);
     }
 
     /// <summary>
@@ -61,7 +53,7 @@ public class SysUserExtOrgService : ITransient
     /// <returns></returns>
     public async Task DeleteUserExtOrgByOrgIdList(List<long> orgIdList)
     {
-        await SysUserExtOrgRep.DeleteAsync(u => orgIdList.Contains(u.OrgId));
+        await _sysUserExtOrgRep.DeleteAsync(u => orgIdList.Contains(u.OrgId));
     }
 
     /// <summary>
@@ -71,7 +63,7 @@ public class SysUserExtOrgService : ITransient
     /// <returns></returns>
     public async Task DeleteUserExtOrgByUserId(long userId)
     {
-        await SysUserExtOrgRep.DeleteAsync(u => u.UserId == userId);
+        await _sysUserExtOrgRep.DeleteAsync(u => u.UserId == userId);
     }
 
     /// <summary>
@@ -81,7 +73,7 @@ public class SysUserExtOrgService : ITransient
     /// <returns></returns>
     public async Task<bool> HasUserOrg(long orgId)
     {
-        return await SysUserExtOrgRep.IsAnyAsync(u => u.OrgId == orgId);
+        return await _sysUserExtOrgRep.IsAnyAsync(u => u.OrgId == orgId);
     }
 
     /// <summary>
@@ -91,6 +83,6 @@ public class SysUserExtOrgService : ITransient
     /// <returns></returns>
     public async Task<bool> HasUserPos(long posId)
     {
-        return await SysUserExtOrgRep.IsAnyAsync(u => u.PosId == posId);
+        return await _sysUserExtOrgRep.IsAnyAsync(u => u.PosId == posId);
     }
 }

+ 10 - 18
Admin.NET/Admin.NET.Core/Service/User/SysUserLdapService.cs

@@ -11,19 +11,11 @@ namespace Admin.NET.Core.Service;
 /// </summary>
 public class SysUserLdapService : ITransient
 {
-    private SqlSugarRepository<SysUserLdap> sysUserLdapRep = null;
+    private readonly SqlSugarRepository<SysUserLdap> _sysUserLdapRep;
 
-    public SysUserLdapService()
+    public SysUserLdapService(SqlSugarRepository<SysUserLdap> sysUserLdapRep)
     {
-    }
-
-    public SqlSugarRepository<SysUserLdap> SysUserLdapRep
-    {
-        get
-        {
-            sysUserLdapRep ??= App.GetRequiredService<SqlSugarRepository<SysUserLdap>>();
-            return sysUserLdapRep;
-        }
+        _sysUserLdapRep = sysUserLdapRep;
     }
 
     /// <summary>
@@ -34,11 +26,11 @@ public class SysUserLdapService : ITransient
     /// <returns></returns>
     public async Task InsertUserLdaps(long tenantId, List<SysUserLdap> sysUserLdaps)
     {
-        await SysUserLdapRep.DeleteAsync(u => u.TenantId == tenantId);
+        await _sysUserLdapRep.DeleteAsync(u => u.TenantId == tenantId);
 
-        await SysUserLdapRep.InsertRangeAsync(sysUserLdaps);
+        await _sysUserLdapRep.InsertRangeAsync(sysUserLdaps);
 
-        await SysUserLdapRep.AsUpdateable()
+        await _sysUserLdapRep.AsUpdateable()
             .InnerJoin<SysUser>((l, u) => l.EmployeeId == u.Account && u.Status == StatusEnum.Enable && u.IsDelete == false && l.IsDelete == false)
             .SetColumns((l, u) => new SysUserLdap { UserId = u.Id })
             .ExecuteCommandAsync();
@@ -54,12 +46,12 @@ public class SysUserLdapService : ITransient
     /// <returns></returns>
     public async Task AddUserLdap(long tenantId, long userId, string account, string domainAccount)
     {
-        var userLdap = await SysUserLdapRep.GetFirstAsync(u => u.TenantId == tenantId && u.IsDelete == false && (u.Account == account || u.UserId == userId || u.EmployeeId == domainAccount));
+        var userLdap = await _sysUserLdapRep.GetFirstAsync(u => u.TenantId == tenantId && u.IsDelete == false && (u.Account == account || u.UserId == userId || u.EmployeeId == domainAccount));
         if (userLdap != null)
-            await SysUserLdapRep.DeleteByIdAsync(userLdap.Id);
+            await _sysUserLdapRep.DeleteByIdAsync(userLdap.Id);
 
         if (!string.IsNullOrWhiteSpace(domainAccount))
-            await SysUserLdapRep.InsertAsync(new SysUserLdap { EmployeeId = account, TenantId = tenantId, UserId = userId, Account = domainAccount });
+            await _sysUserLdapRep.InsertAsync(new SysUserLdap { EmployeeId = account, TenantId = tenantId, UserId = userId, Account = domainAccount });
     }
 
     /// <summary>
@@ -69,6 +61,6 @@ public class SysUserLdapService : ITransient
     /// <returns></returns>
     public async Task DeleteUserLdapByUserId(long userId)
     {
-        await SysUserLdapRep.DeleteAsync(u => u.UserId == userId);
+        await _sysUserLdapRep.DeleteAsync(u => u.UserId == userId);
     }
 }

+ 12 - 19
Admin.NET/Admin.NET.Core/Service/User/SysUserRoleService.cs

@@ -12,20 +12,13 @@ namespace Admin.NET.Core.Service;
 public class SysUserRoleService : ITransient
 {
     private readonly SysCacheService _sysCacheService;
-    private SqlSugarRepository<SysUserRole> sysUserRoleRep = null;
+    private readonly SqlSugarRepository<SysUserRole> _sysUserRoleRep;
 
-    public SysUserRoleService(SysCacheService sysCacheService)
+    public SysUserRoleService(SysCacheService sysCacheService,
+        SqlSugarRepository<SysUserRole> sysUserRoleRep)
     {
         _sysCacheService = sysCacheService;
-    }
-
-    public SqlSugarRepository<SysUserRole> SysUserRoleRep
-    {
-        get
-        {
-            sysUserRoleRep ??= App.GetRequiredService<SqlSugarRepository<SysUserRole>>();
-            return sysUserRoleRep;
-        }
+        _sysUserRoleRep = sysUserRoleRep;
     }
 
     /// <summary>
@@ -35,7 +28,7 @@ public class SysUserRoleService : ITransient
     /// <returns></returns>
     public async Task GrantUserRole(UserRoleInput input)
     {
-        await SysUserRoleRep.DeleteAsync(u => u.UserId == input.UserId);
+        await _sysUserRoleRep.DeleteAsync(u => u.UserId == input.UserId);
 
         if (input.RoleIdList == null || input.RoleIdList.Count < 1) return;
         var roles = input.RoleIdList.Select(u => new SysUserRole
@@ -43,7 +36,7 @@ public class SysUserRoleService : ITransient
             UserId = input.UserId,
             RoleId = u
         }).ToList();
-        await SysUserRoleRep.InsertRangeAsync(roles);
+        await _sysUserRoleRep.InsertRangeAsync(roles);
         _sysCacheService.Remove(CacheConst.KeyUserButton + input.UserId);
     }
 
@@ -54,7 +47,7 @@ public class SysUserRoleService : ITransient
     /// <returns></returns>
     public async Task DeleteUserRoleByRoleId(long roleId)
     {
-        await SysUserRoleRep.AsQueryable()
+        await _sysUserRoleRep.AsQueryable()
              .Where(u => u.RoleId == roleId)
              .Select(u => u.UserId)
              .ForEachAsync(userId =>
@@ -62,7 +55,7 @@ public class SysUserRoleService : ITransient
                  _sysCacheService.Remove(CacheConst.KeyUserButton + userId);
              });
 
-        await SysUserRoleRep.DeleteAsync(u => u.RoleId == roleId);
+        await _sysUserRoleRep.DeleteAsync(u => u.RoleId == roleId);
     }
 
     /// <summary>
@@ -72,7 +65,7 @@ public class SysUserRoleService : ITransient
     /// <returns></returns>
     public async Task DeleteUserRoleByUserId(long userId)
     {
-        await SysUserRoleRep.DeleteAsync(u => u.UserId == userId);
+        await _sysUserRoleRep.DeleteAsync(u => u.UserId == userId);
         _sysCacheService.Remove(CacheConst.KeyUserButton + userId);
     }
 
@@ -83,7 +76,7 @@ public class SysUserRoleService : ITransient
     /// <returns></returns>
     public async Task<List<SysRole>> GetUserRoleList(long userId)
     {
-        var sysUserRoleList = await SysUserRoleRep.AsQueryable()
+        var sysUserRoleList = await _sysUserRoleRep.AsQueryable()
             .Includes(u => u.SysRole)
             .Where(u => u.UserId == userId).ToListAsync();
         return sysUserRoleList.Where(u => u.SysRole != null).Select(u => u.SysRole).ToList();
@@ -96,7 +89,7 @@ public class SysUserRoleService : ITransient
     /// <returns></returns>
     public async Task<List<long>> GetUserRoleIdList(long userId)
     {
-        return await SysUserRoleRep.AsQueryable()
+        return await _sysUserRoleRep.AsQueryable()
             .Where(u => u.UserId == userId).Select(u => u.RoleId).ToListAsync();
     }
 
@@ -107,7 +100,7 @@ public class SysUserRoleService : ITransient
     /// <returns></returns>
     public async Task<List<long>> GetUserIdList(long roleId)
     {
-        return await SysUserRoleRep.AsQueryable()
+        return await _sysUserRoleRep.AsQueryable()
             .Where(u => u.RoleId == roleId).Select(u => u.UserId).ToListAsync();
     }
 }

+ 29 - 32
Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs

@@ -20,7 +20,7 @@ public class SysUserService : IDynamicApiController, ITransient
     private readonly SysOnlineUserService _sysOnlineUserService;
     private readonly SysCacheService _sysCacheService;
     private readonly SysUserLdapService _sysUserLdapService;
-    private SqlSugarRepository<SysUser> sysUserRep = null;
+    private readonly SqlSugarRepository<SysUser> _sysUserRep;
 
     public SysUserService(UserManager userManager,
         SysOrgService sysOrgService,
@@ -29,7 +29,8 @@ public class SysUserService : IDynamicApiController, ITransient
         SysConfigService sysConfigService,
         SysOnlineUserService sysOnlineUserService,
         SysCacheService sysCacheService,
-        SysUserLdapService sysUserLdapService)
+        SysUserLdapService sysUserLdapService,
+        SqlSugarRepository<SysUser> sysUserRep)
     {
         _userManager = userManager;
         _sysOrgService = sysOrgService;
@@ -39,15 +40,7 @@ public class SysUserService : IDynamicApiController, ITransient
         _sysOnlineUserService = sysOnlineUserService;
         _sysCacheService = sysCacheService;
         _sysUserLdapService = sysUserLdapService;
-    }
-
-    public SqlSugarRepository<SysUser> SysUserRep
-    {
-        get
-        {
-            sysUserRep ??= App.GetRequiredService<SqlSugarRepository<SysUser>>();
-            return sysUserRep;
-        }
+        _sysUserRep = sysUserRep;
     }
 
     /// <summary>
@@ -71,7 +64,7 @@ public class SysUserService : IDynamicApiController, ITransient
             orgList = _userManager.SuperAdmin ? null : userOrgIdList;
         }
 
-        return await SysUserRep.AsQueryable()
+        return await _sysUserRep.AsQueryable()
             .LeftJoin<SysOrg>((u, a) => u.OrgId == a.Id)
             .LeftJoin<SysPos>((u, a, b) => u.PosId == b.Id)
             .Where(u => u.AccountType != AccountTypeEnum.SuperAdmin)
@@ -100,14 +93,14 @@ public class SysUserService : IDynamicApiController, ITransient
     [DisplayName("增加用户")]
     public virtual async Task<long> AddUser(AddUserInput input)
     {
-        var isExist = await SysUserRep.AsQueryable().ClearFilter().AnyAsync(u => u.Account == input.Account);
+        var isExist = await _sysUserRep.AsQueryable().ClearFilter().AnyAsync(u => u.Account == input.Account);
         if (isExist) throw Oops.Oh(ErrorCodeEnum.D1003);
 
         var password = await _sysConfigService.GetConfigValue<string>(CommonConst.SysPassword);
 
         var user = input.Adapt<SysUser>();
         user.Password = CryptogramUtil.Encrypt(password);
-        var newUser = await SysUserRep.AsInsertable(user).ExecuteReturnEntityAsync();
+        var newUser = await _sysUserRep.AsInsertable(user).ExecuteReturnEntityAsync();
 
         input.Id = newUser.Id;
         await UpdateRoleAndExtOrg(input);
@@ -129,19 +122,19 @@ public class SysUserService : IDynamicApiController, ITransient
     [DisplayName("更新用户")]
     public virtual async Task UpdateUser(UpdateUserInput input)
     {
-        if (await SysUserRep.AsQueryable().ClearFilter().AnyAsync(u => u.Account == input.Account && u.Id != input.Id))
+        if (await _sysUserRep.AsQueryable().ClearFilter().AnyAsync(u => u.Account == input.Account && u.Id != input.Id))
             throw Oops.Oh(ErrorCodeEnum.D1003);
 
-        await SysUserRep.AsUpdateable(input.Adapt<SysUser>()).IgnoreColumns(true)
+        await _sysUserRep.AsUpdateable(input.Adapt<SysUser>()).IgnoreColumns(true)
             .IgnoreColumns(u => new { u.Password, u.Status }).ExecuteCommandAsync();
 
         await UpdateRoleAndExtOrg(input);
 
         // 删除用户机构缓存
-        SqlSugarFilter.DeleteUserOrgCache(input.Id, SysUserRep.Context.CurrentConnectionConfig.ConfigId.ToString());
+        SqlSugarFilter.DeleteUserOrgCache(input.Id, _sysUserRep.Context.CurrentConnectionConfig.ConfigId.ToString());
 
         // 若账号的角色和组织架构发生变化,则强制下线账号进行权限更新
-        var user = await SysUserRep.AsQueryable().ClearFilter().FirstAsync(u => u.Id == input.Id);
+        var user = await _sysUserRep.AsQueryable().ClearFilter().FirstAsync(u => u.Id == input.Id);
         var roleIds = await GetOwnRoleList(input.Id);
         if (input.OrgId != user.OrgId || !input.RoleIdList.OrderBy(u => u).SequenceEqual(roleIds.OrderBy(u => u)))
             await _sysOnlineUserService.ForceOffline(input.Id);
@@ -171,16 +164,20 @@ public class SysUserService : IDynamicApiController, ITransient
     [DisplayName("删除用户")]
     public virtual async Task DeleteUser(DeleteUserInput input)
     {
-        var user = await SysUserRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
+        var user = await _sysUserRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
         if (user.AccountType == AccountTypeEnum.SuperAdmin)
             throw Oops.Oh(ErrorCodeEnum.D1014);
         if (user.Id == _userManager.UserId)
             throw Oops.Oh(ErrorCodeEnum.D1001);
+        // 若账号为租户默认账号则禁止删除
+        var isTenantUser = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().IsAnyAsync(u => u.UserId == input.Id);
+        if (isTenantUser)
+            throw Oops.Oh(ErrorCodeEnum.D1029);
 
         // 强制下线
         await _sysOnlineUserService.ForceOffline(user.Id);
 
-        await SysUserRep.DeleteAsync(user);
+        await _sysUserRep.DeleteAsync(user);
 
         // 删除用户角色
         await _sysUserRoleService.DeleteUserRoleByUserId(input.Id);
@@ -199,7 +196,7 @@ public class SysUserService : IDynamicApiController, ITransient
     [DisplayName("查看用户基本信息")]
     public virtual async Task<SysUser> GetBaseInfo()
     {
-        return await SysUserRep.GetFirstAsync(u => u.Id == _userManager.UserId);
+        return await _sysUserRep.GetFirstAsync(u => u.Id == _userManager.UserId);
     }
 
     /// <summary>
@@ -210,7 +207,7 @@ public class SysUserService : IDynamicApiController, ITransient
     [DisplayName("更新用户基本信息")]
     public virtual async Task<int> UpdateBaseInfo(SysUser user)
     {
-        return await SysUserRep.AsUpdateable(user)
+        return await _sysUserRep.AsUpdateable(user)
             .IgnoreColumns(u => new { u.CreateTime, u.Account, u.Password, u.AccountType, u.OrgId, u.PosId }).ExecuteCommandAsync();
     }
 
@@ -225,7 +222,7 @@ public class SysUserService : IDynamicApiController, ITransient
         if (_userManager.UserId == input.Id)
             throw Oops.Oh(ErrorCodeEnum.D1026);
 
-        var user = await SysUserRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
+        var user = await _sysUserRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
         if (user.AccountType == AccountTypeEnum.SuperAdmin)
             throw Oops.Oh(ErrorCodeEnum.D1015);
 
@@ -247,7 +244,7 @@ public class SysUserService : IDynamicApiController, ITransient
         }
 
         user.Status = input.Status;
-        return await SysUserRep.AsUpdateable(user).UpdateColumns(u => new { u.Status }).ExecuteCommandAsync();
+        return await _sysUserRep.AsUpdateable(user).UpdateColumns(u => new { u.Status }).ExecuteCommandAsync();
     }
 
     /// <summary>
@@ -278,7 +275,7 @@ public class SysUserService : IDynamicApiController, ITransient
         input.PasswordOld = CryptogramUtil.SM2Decrypt(input.PasswordOld);
         input.PasswordNew = CryptogramUtil.SM2Decrypt(input.PasswordNew);
 
-        var user = await SysUserRep.GetFirstAsync(u => u.Id == _userManager.UserId) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
+        var user = await _sysUserRep.GetFirstAsync(u => u.Id == _userManager.UserId) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
         if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString())
         {
             if (user.Password != MD5Encryption.Encrypt(input.PasswordOld))
@@ -305,7 +302,7 @@ public class SysUserService : IDynamicApiController, ITransient
             user.Password = CryptogramUtil.Encrypt(input.PasswordNew);
         }
 
-        return await SysUserRep.AsUpdateable(user).UpdateColumns(u => u.Password).ExecuteCommandAsync();
+        return await _sysUserRep.AsUpdateable(user).UpdateColumns(u => u.Password).ExecuteCommandAsync();
     }
 
     /// <summary>
@@ -316,13 +313,13 @@ public class SysUserService : IDynamicApiController, ITransient
     [DisplayName("重置用户密码")]
     public virtual async Task<string> ResetPwd(ResetPwdUserInput input)
     {
-        var user = await SysUserRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
+        var user = await _sysUserRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
         var password = await _sysConfigService.GetConfigValue<string>(CommonConst.SysPassword);
         user.Password = CryptogramUtil.Encrypt(password);
-        await SysUserRep.AsUpdateable(user).UpdateColumns(u => u.Password).ExecuteCommandAsync();
+        await _sysUserRep.AsUpdateable(user).UpdateColumns(u => u.Password).ExecuteCommandAsync();
 
         // 清空密码错误次数
-        var keyErrorPasswordCount = $"{CacheConst.KeyErrorPasswordCount}{user.Account}";
+        var keyErrorPasswordCount = $"{CacheConst.KeyPasswordErrorTimes}{user.Account}";
         _sysCacheService.Remove(keyErrorPasswordCount);
 
         return password;
@@ -336,11 +333,11 @@ public class SysUserService : IDynamicApiController, ITransient
     [DisplayName("解除登录锁定")]
     public virtual async Task UnlockLogin(UnlockLoginInput input)
     {
-        var user = await SysUserRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
+        var user = await _sysUserRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
 
         // 清空密码错误次数
-        var keyErrorPasswordCount = $"{CacheConst.KeyErrorPasswordCount}{user.Account}";
-        _sysCacheService.Remove(keyErrorPasswordCount);
+        var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{user.Account}";
+        _sysCacheService.Remove(keyPasswordErrorTimes);
     }
 
     /// <summary>

+ 47 - 1
Admin.NET/Admin.NET.Core/Service/Wechat/Dto/WechatPayInput.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -32,6 +32,16 @@ public class WechatPayTransactionInput
     /// 优惠标记
     /// </summary>
     public string GoodsTag { get; set; }
+
+    /// <summary>
+    /// 业务标签,用来区分做什么业务
+    /// </summary>
+    public string Tags { get; set; }
+
+    /// <summary>
+    /// 对应业务的主键
+    /// </summary>
+    public long BusinessId { get; set; }
 }
 
 public class WechatPayParaInput
@@ -40,4 +50,40 @@ public class WechatPayParaInput
     /// 订单Id
     /// </summary>
     public string PrepayId { get; set; }
+}
+
+public class WechatPayRefundDomesticInput
+{
+    /// <summary>
+    /// 商户端生成的业务流水号
+    /// </summary>
+    [Required]
+    public string TradeId { get; set; }
+
+    /// <summary>
+    /// 退款原因
+    /// </summary>
+    public string Reason { get; set; }
+
+    /// <summary>
+    /// 退款金额
+    /// </summary>
+    [Required]
+    public int Refund { get; set; }
+
+    /// <summary>
+    /// 原订单金额
+    /// </summary>
+    [Required]
+    public int Total { get; set; }
+}
+
+public class WechatPayPageInput : BasePageInput
+{
+    public string SearchKey { get; set; }
+
+    /// <summary>
+    /// 添加时间范围
+    /// </summary>
+    public List<DateTime?> CreateTimeRange { get; set; }
 }

+ 25 - 1
Admin.NET/Admin.NET.Core/Service/Wechat/Dto/WechatPayOutput.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -27,4 +27,28 @@ public class WechatPayOutput
     /// 优惠标记
     /// </summary>
     public string GoodsTag { get; set; }
+}
+
+public class WechatPayTransactionOutput
+{
+    public string PrepayId { get; set; }
+
+    public string OutTradeNumber { get; set; }
+
+    public WechatPayParaOutput SingInfo { get; set; }
+}
+
+public class WechatPayParaOutput
+{
+    public string AppId { get; set; }
+
+    public string TimeStamp { get; set; }
+
+    public string NonceStr { get; set; }
+
+    public string Package { get; set; }
+
+    public string SignType { get; set; }
+
+    public string PaySign { get; set; }
 }

+ 315 - 46
Admin.NET/Admin.NET.Core/Service/Wechat/SysWechatPayService.cs

@@ -1,9 +1,12 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
+using Furion.Logging.Extensions;
+using Newtonsoft.Json;
+
 namespace Admin.NET.Core.Service;
 
 /// <summary>
@@ -12,7 +15,8 @@ namespace Admin.NET.Core.Service;
 [ApiDescriptionSettings(Order = 210)]
 public class SysWechatPayService : IDynamicApiController, ITransient
 {
-    private readonly SqlSugarRepository<SysWechatPay> _sysWechatPayUserRep;
+    private readonly SqlSugarRepository<SysWechatPay> _sysWechatPayRep;
+    private readonly SqlSugarRepository<SysWechatRefund> _sysWechatRefundRep;
 
     private readonly WechatPayOptions _wechatPayOptions;
     private readonly PayCallBackOptions _payCallBackOptions;
@@ -20,10 +24,12 @@ public class SysWechatPayService : IDynamicApiController, ITransient
     private readonly WechatTenpayClient _wechatTenpayClient;
 
     public SysWechatPayService(SqlSugarRepository<SysWechatPay> sysWechatPayUserRep,
+        SqlSugarRepository<SysWechatRefund> sysWechatRefundRep,
         IOptions<WechatPayOptions> wechatPayOptions,
         IOptions<PayCallBackOptions> payCallBackOptions)
     {
-        _sysWechatPayUserRep = sysWechatPayUserRep;
+        _sysWechatPayRep = sysWechatPayUserRep;
+        _sysWechatRefundRep = sysWechatRefundRep;
         _wechatPayOptions = wechatPayOptions.Value;
         _payCallBackOptions = payCallBackOptions.Value;
 
@@ -49,22 +55,61 @@ public class SysWechatPayService : IDynamicApiController, ITransient
         return new WechatTenpayClient(tenpayClientOptions);
     }
 
+    /// <summary>
+    /// 分页查询支付列表 🔖
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Page")]
+    public async Task<SqlSugarPagedList<SysWechatPay>> Page(WechatPayPageInput input)
+    {
+        var query = _sysWechatPayRep.AsQueryable()
+            .WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), u => u.OutTradeNumber == input.SearchKey || u.TransactionId == input.SearchKey)
+            .WhereIF(input.CreateTimeRange != null && input.CreateTimeRange.Count > 0 && input.CreateTimeRange[0].HasValue, x => x.CreateTime >= input.CreateTimeRange[0])
+            .WhereIF(input.CreateTimeRange != null && input.CreateTimeRange.Count > 1 && input.CreateTimeRange[1].HasValue, x => x.CreateTime < ((DateTime)input.CreateTimeRange[1]).AddDays(1));
+        return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
+    }
+
+    /// <summary>
+    /// 查询退款信息列表
+    /// </summary>
+    /// <param name="id"></param>
+    /// <returns></returns>
+    [HttpPost]
+    [DisplayName("根据支付id获取退款信息列表")]
+    public async Task<List<SysWechatRefund>> ListRefund([FromBody] string id)
+    {
+        var query = _sysWechatRefundRep.AsQueryable()
+            .Where(u => u.TransactionId == id);
+        return await query.ToListAsync();
+    }
+
     /// <summary>
     /// 生成JSAPI调起支付所需参数 🔖
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
     [DisplayName("生成JSAPI调起支付所需参数")]
-    public dynamic GenerateParametersForJsapiPay(WechatPayParaInput input)
+    public WechatPayParaOutput GenerateParametersForJsapiPay(WechatPayParaInput input)
     {
-        return _wechatTenpayClient.GenerateParametersForJsapiPayRequest(_wechatPayOptions.AppId, input.PrepayId);
+        var data = _wechatTenpayClient.GenerateParametersForJsapiPayRequest(_wechatPayOptions.AppId, input.PrepayId);
+        return new WechatPayParaOutput()
+        {
+            AppId = data["appId"],
+            TimeStamp = data["timeStamp"],
+            NonceStr = data["nonceStr"],
+            Package = data["package"],
+            SignType = data["signType"],
+            PaySign = data["paySign"]
+        };
     }
 
     /// <summary>
-    /// 微信支付统一下单获取Id(商户直连) 🔖
+    /// 微信支付下单(商户直连) 🔖
     /// </summary>
-    [DisplayName("微信支付统一下单获取Id(商户直连)")]
-    public async Task<dynamic> CreatePayTransaction([FromBody] WechatPayTransactionInput input)
+    [DisplayName("微信支付下单(商户直连)")]
+    public async Task<WechatPayTransactionOutput> CreatePayTransaction([FromBody] WechatPayTransactionInput input)
     {
         var request = new CreatePayTransactionJsapiRequest()
         {
@@ -82,6 +127,7 @@ public class SysWechatPayService : IDynamicApiController, ITransient
         if (!response.IsSuccessful())
             throw Oops.Oh(response.ErrorMessage);
 
+        var singInfo = this.GenerateParametersForJsapiPay(new WechatPayParaInput() { PrepayId = response.PrepayId });
         // 保存订单信息
         var wechatPay = new SysWechatPay()
         {
@@ -93,21 +139,73 @@ public class SysWechatPayService : IDynamicApiController, ITransient
             GoodsTag = input.GoodsTag,
             Total = input.Total,
             OpenId = input.OpenId,
-            TransactionId = ""
+            TransactionId = "",
+            Tags = input.Tags,
+            BusinessId = input.BusinessId,
         };
-        await _sysWechatPayUserRep.InsertAsync(wechatPay);
+        await _sysWechatPayRep.InsertAsync(wechatPay);
+
+        return new WechatPayTransactionOutput()
+        {
+            PrepayId = response.PrepayId,
+            OutTradeNumber = request.OutTradeNumber,
+            SingInfo = singInfo
+        };
+    }
 
+    /// <summary>
+    /// 微信支付下单(商户直连)Native
+    /// </summary>
+    [DisplayName("微信支付下单(商户直连)Native")]
+    public async Task<dynamic> CreatePayTransactionNative([FromBody] WechatPayTransactionInput input)
+    {
+        var request = new CreatePayTransactionNativeRequest()
+        {
+            OutTradeNumber = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff") + (new Random()).Next(100, 1000), // 订单号
+            AppId = _wechatPayOptions.AppId,
+            Description = input.Description,
+            Attachment = input.Attachment,
+            GoodsTag = input.GoodsTag,
+            ExpireTime = DateTimeOffset.Now.AddMinutes(10),
+            NotifyUrl = _payCallBackOptions.WechatPayUrl,
+            Amount = new CreatePayTransactionNativeRequest.Types.Amount() { Total = input.Total },
+            //Payer = new CreatePayTransactionNativeRequest.Types.Payer() { OpenId = input.OpenId }
+            Scene = new CreatePayTransactionNativeRequest.Types.Scene() { ClientIp = "127.0.0.1" }
+        };
+        var response = await _wechatTenpayClient.ExecuteCreatePayTransactionNativeAsync(request);
+        if (!response.IsSuccessful())
+        {
+            JsonConvert.SerializeObject(response).LogInformation();
+            throw Oops.Oh(response.ErrorMessage);
+        }
+        // 保存订单信息
+        var wechatPay = new SysWechatPay()
+        {
+            AppId = _wechatPayOptions.AppId,
+            MerchantId = _wechatPayOptions.MerchantId,
+            OutTradeNumber = request.OutTradeNumber,
+            Description = input.Description,
+            Attachment = input.Attachment,
+            GoodsTag = input.GoodsTag,
+            Total = input.Total,
+            //OpenId = input.OpenId,
+            TransactionId = "",
+            QrcodeContent = response.QrcodeUrl,
+            Tags = input.Tags,
+            BusinessId = input.BusinessId,
+        };
+        await _sysWechatPayRep.InsertAsync(wechatPay);
         return new
         {
-            response.PrepayId,
-            request.OutTradeNumber
+            request.OutTradeNumber,
+            response.QrcodeUrl
         };
     }
 
     /// <summary>
-    /// 微信支付统一下单获取Id(服务商模式) 🔖
+    /// 微信支付下单(服务商模式) 🔖
     /// </summary>
-    [DisplayName("微信支付统一下单获取Id(服务商模式)")]
+    [DisplayName("微信支付下单(服务商模式)")]
     public async Task<dynamic> CreatePayPartnerTransaction([FromBody] WechatPayTransactionInput input)
     {
         var request = new CreatePayPartnerTransactionJsapiRequest()
@@ -128,7 +226,7 @@ public class SysWechatPayService : IDynamicApiController, ITransient
         var response = await _wechatTenpayClient.ExecuteCreatePayPartnerTransactionJsapiAsync(request);
         if (!response.IsSuccessful())
             throw Oops.Oh(response.ErrorMessage);
-
+        var singInfo = this.GenerateParametersForJsapiPay(new WechatPayParaInput() { PrepayId = response.PrepayId });
         // 保存订单信息
         var wechatPay = new SysWechatPay()
         {
@@ -144,28 +242,167 @@ public class SysWechatPayService : IDynamicApiController, ITransient
             OpenId = input.OpenId,
             TransactionId = ""
         };
-        await _sysWechatPayUserRep.InsertAsync(wechatPay);
+        await _sysWechatPayRep.InsertAsync(wechatPay);
 
         return new
         {
             response.PrepayId,
-            request.OutTradeNumber
+            request.OutTradeNumber,
+            singInfo
         };
     }
 
     /// <summary>
-    /// 获取支付订单详情 🔖
+    /// 获取支付订单详情(本地库) 🔖
     /// </summary>
     /// <param name="tradeId"></param>
     /// <returns></returns>
-    [DisplayName("获取支付订单详情")]
+    [DisplayName("获取支付订单详情(本地库)")]
     public async Task<SysWechatPay> GetPayInfo(string tradeId)
     {
-        return await _sysWechatPayUserRep.GetFirstAsync(u => u.OutTradeNumber == tradeId);
+        return await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == tradeId);
+    }
+
+    /// <summary>
+    /// 获取支付订单详情(微信接口) 🔖
+    /// </summary>
+    /// <param name="tradeId"></param>
+    /// <returns></returns>
+    [DisplayName("获取支付订单详情(微信接口)")]
+    public async Task<SysWechatPay> GetPayInfoFromWechat(string tradeId)
+    {
+        var request = new GetPayTransactionByOutTradeNumberRequest();
+        request.OutTradeNumber = tradeId;
+        var response = await _wechatTenpayClient.ExecuteGetPayTransactionByOutTradeNumberAsync(request);
+        // 修改订单支付状态
+        var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == response.OutTradeNumber
+            && u.MerchantId == response.MerchantId);
+        // 如果状态不一致就更新数据库中的记录
+        if (wechatPay != null && wechatPay.TradeState != response.TradeState)
+        {
+            wechatPay.OpenId = response.Payer.OpenId;
+            wechatPay.TransactionId = response.TransactionId; // 支付订单号
+            wechatPay.TradeType = response.TradeType; // 交易类型
+            wechatPay.TradeState = response.TradeState; // 交易状态
+            wechatPay.TradeStateDescription = response.TradeStateDescription; // 交易状态描述
+            wechatPay.BankType = response.BankType; // 付款银行类型
+            wechatPay.Total = response.Amount.Total; // 订单总金额
+            wechatPay.PayerTotal = response.Amount.PayerTotal; // 用户支付金额
+            wechatPay.SuccessTime = response.SuccessTime.Value.DateTime; // 支付完成时间
+            await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync();
+        }
+        wechatPay = new SysWechatPay()
+        {
+            AppId = _wechatPayOptions.AppId,
+            MerchantId = _wechatPayOptions.MerchantId,
+            SubAppId = _wechatPayOptions.AppId,
+            SubMerchantId = _wechatPayOptions.MerchantId,
+            OutTradeNumber = request.OutTradeNumber,
+            Attachment = response.Attachment,
+            Total = response.Amount.Total, // 订单总金额
+            TransactionId = response.TransactionId,
+            TradeType = response.TradeType, // 交易类型
+            TradeState = response.TradeState, // 交易状态
+            TradeStateDescription = response.TradeStateDescription, // 交易状态描述
+            BankType = response.BankType, // 付款银行类型
+            PayerTotal = response.Amount.PayerTotal, // 用户支付金额
+            SuccessTime = response.SuccessTime.Value.DateTime // 支付完成时间
+        };
+        return wechatPay;
     }
 
     /// <summary>
-    /// 微信支付成功回调(商户直连) 🔖
+    /// 退款申请
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [DisplayName("退款申请")]
+    [HttpPost]
+    public async Task<dynamic> CreateRefundDomestic([FromBody] WechatPayRefundDomesticInput input)
+    {
+        // refund/domestic/refunds
+        var request = new CreateRefundDomesticRefundRequest()
+        {
+            Amount = new CreateRefundDomesticRefundRequest.Types.Amount()
+            {
+                Refund = input.Refund,
+                Total = input.Total,
+                Currency = "CNY"
+            },
+
+            OutTradeNumber = input.TradeId,
+            OutRefundNumber = "R" + DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff") + (new Random()).Next(100, 1000), // 订单号
+            NotifyUrl = _payCallBackOptions.WechatPayUrl,
+            Reason = input.Reason,
+        };
+        var response = await _wechatTenpayClient.ExecuteCreateRefundDomesticRefundAsync(request);
+        if (string.IsNullOrEmpty(response.ErrorCode))
+        {
+            // 成功了,这里应该保存退款订单信息
+            var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == response.OutTradeNumber);
+            // 保存订单信息
+            if (wechatPay != null)
+            {
+                var wechatRefund = new SysWechatRefund()
+                {
+                    WechatPayId = wechatPay.Id,
+                    TransactionId = response.TransactionId,
+                    Refund = input.Refund,
+                    Reason = input.Reason,
+                    OutRefundNumber = request.OutRefundNumber,
+                    Channel = response.Channel,
+                    UserReceivedAccount = response.UserReceivedAccount,
+                };
+                await _sysWechatRefundRep.InsertAsync(wechatRefund);
+            }
+        }
+        else
+        {
+            throw Oops.Bah($"[{response.ErrorCode}]{response.ErrorMessage}");
+        }
+        return response;
+    }
+
+    /// <summary>
+    /// 获取退款订单详情(微信接口)
+    /// </summary>
+    /// <param name="refundId"></param>
+    /// <returns></returns>
+    [DisplayName("获取退款订单详情(微信接口)")]
+    public async Task<SysWechatRefund> GetRefundInfoFromWechat(string refundId)
+    {
+        var request = new GetRefundDomesticRefundByOutRefundNumberRequest();
+        request.OutRefundNumber = refundId;
+        var response = await _wechatTenpayClient.ExecuteGetRefundDomesticRefundByOutRefundNumberAsync(request);
+        // 修改订单支付状态
+        var wechatRefund = await _sysWechatRefundRep.GetFirstAsync(u => u.OutRefundNumber == refundId);
+        // 如果状态不一致就更新数据库中的记录
+        if (wechatRefund != null && wechatRefund.TradeState != response.Status)
+        {
+            wechatRefund.TransactionId = response.TransactionId; // 支付订单号
+            wechatRefund.TradeState = response.Status; // 交易状态
+            wechatRefund.SuccessTime = response.SuccessTime.Value.DateTime; // 支付完成时间
+            await _sysWechatRefundRep.AsUpdateable(wechatRefund).IgnoreColumns(true).ExecuteCommandAsync();
+            // 有退款,刷新一下订单状态
+            var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.Id == wechatRefund.WechatPayId);
+            if (wechatPay != null)
+                await GetPayInfoFromWechat(wechatPay.OutTradeNumber);
+        }
+        wechatRefund = new SysWechatRefund()
+        {
+            TransactionId = response.TransactionId,
+            Refund = response.Amount.Refund,
+            OutRefundNumber = request.OutRefundNumber,
+            Channel = response.Channel,
+            UserReceivedAccount = response.UserReceivedAccount,
+            TradeState = response.Status, // 交易状态
+            SuccessTime = response.SuccessTime.Value.DateTime, // 支付完成时间
+        };
+        return wechatRefund;
+    }
+
+    /// <summary>
+    /// 微信支付成功回调(商户直连)
     /// </summary>
     /// <returns></returns>
     [AllowAnonymous]
@@ -180,32 +417,64 @@ public class SysWechatPayService : IDynamicApiController, ITransient
         var callbackModel = _wechatTenpayClient.DeserializeEvent(callbackJson);
         if ("TRANSACTION.SUCCESS".Equals(callbackModel.EventType))
         {
-            var callbackResource = _wechatTenpayClient.DecryptEventResource<TransactionResource>(callbackModel);
+            try
+            {
+                var callbackPayResource = _wechatTenpayClient.DecryptEventResource<TransactionResource>(callbackModel);
 
-            // 修改订单支付状态
-            var wechatPay = await _sysWechatPayUserRep.GetFirstAsync(u => u.OutTradeNumber == callbackResource.OutTradeNumber
-                && u.MerchantId == callbackResource.MerchantId);
-            if (wechatPay == null) return null;
-            //wechatPay.OpenId = callbackResource.Payer.OpenId; // 支付者标识
-            //wechatPay.MerchantId = callbackResource.MerchantId; // 微信商户号
-            //wechatPay.OutTradeNumber = callbackResource.OutTradeNumber; // 商户订单号
-            wechatPay.TransactionId = callbackResource.TransactionId; // 支付订单号
-            wechatPay.TradeType = callbackResource.TradeType; // 交易类型
-            wechatPay.TradeState = callbackResource.TradeState; // 交易状态
-            wechatPay.TradeStateDescription = callbackResource.TradeStateDescription; // 交易状态描述
-            wechatPay.BankType = callbackResource.BankType; // 付款银行类型
-            wechatPay.Total = callbackResource.Amount.Total; // 订单总金额
-            wechatPay.PayerTotal = callbackResource.Amount.PayerTotal; // 用户支付金额
-            wechatPay.SuccessTime = callbackResource.SuccessTime; // 支付完成时间
+                // 修改订单支付状态
+                var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == callbackPayResource.OutTradeNumber
+                    && u.MerchantId == callbackPayResource.MerchantId);
+                if (wechatPay == null) return null;
+                wechatPay.OpenId = callbackPayResource.Payer.OpenId; // 支付者标识
+                //wechatPay.MerchantId = callbackResource.MerchantId; // 微信商户号
+                //wechatPay.OutTradeNumber = callbackResource.OutTradeNumber; // 商户订单号
+                wechatPay.TransactionId = callbackPayResource.TransactionId; // 支付订单号
+                wechatPay.TradeType = callbackPayResource.TradeType; // 交易类型
+                wechatPay.TradeState = callbackPayResource.TradeState; // 交易状态
+                wechatPay.TradeStateDescription = callbackPayResource.TradeStateDescription; // 交易状态描述
+                wechatPay.BankType = callbackPayResource.BankType; // 付款银行类型
+                wechatPay.Total = callbackPayResource.Amount.Total; // 订单总金额
+                wechatPay.PayerTotal = callbackPayResource.Amount.PayerTotal; // 用户支付金额
+                wechatPay.SuccessTime = callbackPayResource.SuccessTime.DateTime; // 支付完成时间
 
-            await _sysWechatPayUserRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync();
+                await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync();
 
-            return new WechatPayOutput()
+                return new WechatPayOutput()
+                {
+                    Total = wechatPay.Total,
+                    Attachment = wechatPay.Attachment,
+                    GoodsTag = wechatPay.GoodsTag
+                };
+            }
+            catch (Exception ex)
             {
-                Total = wechatPay.Total,
-                Attachment = wechatPay.Attachment,
-                GoodsTag = wechatPay.GoodsTag
-            };
+                "微信支付回调时出错:".LogError(ex);
+            }
+        }
+        else if ("REFUND.SUCCESS".Equals(callbackModel.EventType))
+        {
+            //参考:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/refund-result-notice.html
+            try
+            {
+                var callbackRefundResource = _wechatTenpayClient.DecryptEventResource<RefundResource>(callbackModel);
+                // 修改订单支付状态
+                var wechatRefund = await _sysWechatRefundRep.GetFirstAsync(u => u.OutRefundNumber == callbackRefundResource.OutRefundNumber);
+                if (wechatRefund == null) return null;
+                wechatRefund.TradeState = callbackRefundResource.RefundStatus; // 交易状态
+                wechatRefund.SuccessTime = callbackRefundResource.SuccessTime.Value.DateTime; // 支付完成时间
+
+                await _sysWechatRefundRep.AsUpdateable(wechatRefund).IgnoreColumns(true).ExecuteCommandAsync();
+                // 有退款,刷新一下订单状态
+                await GetPayInfoFromWechat(callbackRefundResource.OutTradeNumber);
+            }
+            catch (Exception ex)
+            {
+                "微信退款回调时出错:".LogError(ex);
+            }
+        }
+        else
+        {
+            callbackModel.EventType.LogInformation();
         }
 
         return null;
@@ -230,7 +499,7 @@ public class SysWechatPayService : IDynamicApiController, ITransient
             var callbackResource = _wechatTenpayClient.DecryptEventResource<PartnerTransactionResource>(callbackModel);
 
             // 修改订单支付状态
-            var wechatPay = await _sysWechatPayUserRep.GetFirstAsync(u => u.OutTradeNumber == callbackResource.OutTradeNumber
+            var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == callbackResource.OutTradeNumber
                 && u.MerchantId == callbackResource.MerchantId);
             if (wechatPay == null) return;
             //wechatPay.OpenId = callbackResource.Payer.OpenId; // 支付者标识
@@ -243,9 +512,9 @@ public class SysWechatPayService : IDynamicApiController, ITransient
             wechatPay.BankType = callbackResource.BankType; // 付款银行类型
             wechatPay.Total = callbackResource.Amount.Total; // 订单总金额
             wechatPay.PayerTotal = callbackResource.Amount.PayerTotal; // 用户支付金额
-            wechatPay.SuccessTime = callbackResource.SuccessTime; // 支付完成时间
+            wechatPay.SuccessTime = callbackResource.SuccessTime.DateTime; // 支付完成时间
 
-            await _sysWechatPayUserRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync();
+            await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync();
         }
     }
 }

+ 1 - 1
Admin.NET/Admin.NET.Core/Service/Wechat/SysWechatService.cs

@@ -168,7 +168,7 @@ public class SysWechatService : IDynamicApiController, ITransient
             Url = input.Url,
             MiniProgram = new CgibinMessageTemplateSendRequest.Types.MiniProgram
             {
-                AppId = _wechatApiClientFactory._wechatOptions.WechatAppId,
+                AppId = _wechatApiClientFactory._wechatOptions.WxOpenAppId,
                 PagePath = input.MiniProgramPagePath,
             },
             Data = messageData

+ 15 - 17
Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -133,10 +133,7 @@ public static class SqlSugarSetup
             {
                 if (ex.Parametres == null) return;
                 var log = $"【{DateTime.Now}——错误SQL】\r\n{UtilMethods.GetNativeSql(ex.Sql, (SugarParameter[])ex.Parametres)}\r\n";
-                var originColor = Console.ForegroundColor;
-                Console.ForegroundColor = ConsoleColor.DarkRed;
-                Console.WriteLine(log);
-                Console.ForegroundColor = originColor;
+                Log.Error(log, ex);
                 App.PrintToMiniProfiler("SqlSugar", "Error", log);
             };
             db.Aop.OnLogExecuted = (sql, pars) =>
@@ -156,10 +153,7 @@ public static class SqlSugarSetup
                     var fileLine = db.Ado.SqlStackTrace.FirstLine; // 行号
                     var firstMethodName = db.Ado.SqlStackTrace.FirstMethodName; // 方法名
                     var log = $"【{DateTime.Now}——超时SQL】\r\n【所在文件名】:{fileName}\r\n【代码行数】:{fileLine}\r\n【方法名】:{firstMethodName}\r\n" + $"【SQL语句】:{UtilMethods.GetNativeSql(sql, pars)}";
-                    var originColor = Console.ForegroundColor;
-                    Console.ForegroundColor = ConsoleColor.DarkYellow;
-                    Console.WriteLine(log);
-                    Console.ForegroundColor = originColor;
+                    Log.Warning(log);
                     App.PrintToMiniProfiler("SqlSugar", "Slow", log);
                 }
             };
@@ -171,7 +165,7 @@ public static class SqlSugarSetup
             if (entityInfo.OperationType == DataFilterType.InsertByObject)
             {
                 // 若主键是长整型且空则赋值雪花Id
-                if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.EntityColumnInfo.PropertyInfo.PropertyType == typeof(long))
+                if (entityInfo.EntityColumnInfo.IsPrimarykey && !entityInfo.EntityColumnInfo.IsIdentity && entityInfo.EntityColumnInfo.PropertyInfo.PropertyType == typeof(long))
                 {
                     var id = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue);
                     if (id == null || (long)id == 0)
@@ -185,33 +179,34 @@ public static class SqlSugarSetup
                 // 若当前用户非空(web线程时)
                 if (App.User != null)
                 {
+                    dynamic entityValue = entityInfo.EntityValue;
                     if (entityInfo.PropertyName == nameof(EntityTenantId.TenantId))
                     {
-                        var tenantId = ((dynamic)entityInfo.EntityValue).TenantId;
+                        var tenantId = entityValue.TenantId;
                         if (tenantId == null || tenantId == 0)
                             entityInfo.SetValue(App.User.FindFirst(ClaimConst.TenantId)?.Value);
                     }
                     else if (entityInfo.PropertyName == nameof(EntityBase.CreateUserId))
                     {
-                        var createUserId = ((dynamic)entityInfo.EntityValue).CreateUserId;
+                        var createUserId = entityValue.CreateUserId;
                         if (createUserId == 0 || createUserId == null)
                             entityInfo.SetValue(App.User.FindFirst(ClaimConst.UserId)?.Value);
                     }
                     else if (entityInfo.PropertyName == nameof(EntityBase.CreateUserName))
                     {
-                        var createUserName = ((dynamic)entityInfo.EntityValue).CreateUserName;
+                        var createUserName = entityValue.CreateUserName;
                         if (string.IsNullOrEmpty(createUserName))
                             entityInfo.SetValue(App.User.FindFirst(ClaimConst.RealName)?.Value);
                     }
                     else if (entityInfo.PropertyName == nameof(EntityBaseData.CreateOrgId))
                     {
-                        var createOrgId = ((dynamic)entityInfo.EntityValue).CreateOrgId;
+                        var createOrgId = entityValue.CreateOrgId;
                         if (createOrgId == 0 || createOrgId == null)
                             entityInfo.SetValue(App.User.FindFirst(ClaimConst.OrgId)?.Value);
                     }
                     else if (entityInfo.PropertyName == nameof(EntityBaseData.CreateOrgName))
                     {
-                        var createOrgName = ((dynamic)entityInfo.EntityValue).CreateOrgName;
+                        var createOrgName = entityValue.CreateOrgName;
                         if (string.IsNullOrEmpty(createOrgName))
                             entityInfo.SetValue(App.User.FindFirst(ClaimConst.OrgName)?.Value);
                     }
@@ -323,7 +318,8 @@ public static class SqlSugarSetup
         if (config.SeedSettings.EnableInitSeed)
         {
             var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>))))
-                .WhereIF(config.SeedSettings.EnableIncreSeed, u => u.IsDefined(typeof(IncreSeedAttribute), false)).ToList();
+                .WhereIF(config.SeedSettings.EnableIncreSeed, u => u.IsDefined(typeof(IncreSeedAttribute), false))
+                .OrderBy(u => u.GetCustomAttributes(typeof(SeedDataAttribute), false).Length > 0 ? (u.GetCustomAttributes(typeof(SeedDataAttribute), false)[0] as SeedDataAttribute).Order : 0).ToList();
 
             foreach (var seedType in seedDataTypes)
             {
@@ -383,7 +379,9 @@ public static class SqlSugarSetup
         db.DbMaintenance.CreateDatabase();
 
         // 获取所有业务表-初始化租户库表结构(排除系统表、日志表、特定库表)
-        var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) &&
+        var entityTypes = App.EffectiveTypes
+            .Where(u => !u.GetCustomAttributes<IgnoreTableAttribute>().Any())
+            .Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) &&
             !u.IsDefined(typeof(SysTableAttribute), false) && !u.IsDefined(typeof(LogTableAttribute), false) && !u.IsDefined(typeof(TenantAttribute), false)).ToList();
         if (!entityTypes.Any()) return;
 

+ 1 - 1
Admin.NET/Admin.NET.Core/Util/CodeGenUtil.cs

@@ -242,7 +242,7 @@ public static class CodeGenUtil
             "bit" => "bool",
             "money" or "smallmoney" or "numeric" or "decimal" => "decimal",
             "real" => "Single",
-            "datetime" or "smalldatetime" => "DateTime",
+            "datetime" or "datetime2" or "smalldatetime" => "DateTime",
             "float" or "double" => "double",
             "image" or "binary" or "varbinary" => "byte[]",
             "uniqueidentifier" => "Guid",

+ 68 - 24
Admin.NET/Admin.NET.Core/Util/CommonUtil.cs

@@ -4,6 +4,7 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
+using IPTools.Core;
 using Magicodes.ExporterAndImporter.Core.Models;
 using System.Xml;
 using System.Xml.Linq;
@@ -104,24 +105,24 @@ public static class CommonUtil
     /// 导出模板Excel
     /// </summary>
     /// <returns></returns>
-    public static async Task<IActionResult> ExportExcelTemplate<T>() where T : class, new()
+    public static async Task<IActionResult> ExportExcelTemplate<T>(string fileName = null) where T : class, new()
     {
         IImporter importer = new ExcelImporter();
         var res = await importer.GenerateTemplateBytes<T>();
 
-        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = typeof(T).Name + ".xlsx" };
+        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = $"{(string.IsNullOrEmpty(fileName) ? typeof(T).Name : fileName)}.xlsx" };
     }
 
     /// <summary>
     /// 导出数据excel
     /// </summary>
     /// <returns></returns>
-    public static async Task<IActionResult> ExportExcelData<T>(ICollection<T> data) where T : class, new()
+    public static async Task<IActionResult> ExportExcelData<T>(ICollection<T> data, string fileName = null) where T : class, new()
     {
         var export = new ExcelExporter();
         var res = await export.ExportAsByteArray<T>(data);
 
-        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = typeof(T).Name + ".xlsx" };
+        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = $"{(string.IsNullOrEmpty(fileName) ? typeof(T).Name : fileName)}.xlsx" };
     }
 
     /// <summary>
@@ -275,25 +276,25 @@ public static class CommonUtil
         // 整理导入对象的属性名称,<字典数据,原属性信息,目标属性信息>
         var propMappings = new Dictionary<string, Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>>();
 
-        var dictService = App.GetService<SqlSugarRepository<SysDictData>>();
+        var dictService = App.GetRequiredService<SqlSugarRepository<SysDictData>>();
         var tSourceProps = typeof(TSource).GetProperties().ToList();
-        var tTargetProps = typeof(TTarget).GetProperties().ToDictionary(m => m.Name);
+        var tTargetProps = typeof(TTarget).GetProperties().ToDictionary(u => u.Name);
         foreach (var propertyInfo in tSourceProps)
         {
             var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
             if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
             {
                 var targetProp = tTargetProps[attrs.TargetPropName];
-                var mappingValues = dictService.Context.Queryable<SysDictType, SysDictData>((a, b) =>
-                    new JoinQueryInfos(JoinType.Inner, a.Id == b.DictTypeId))
-                    .Where(a => a.Code == attrs.TypeCode)
-                    .Where((a, b) => a.Status == StatusEnum.Enable && b.Status == StatusEnum.Enable)
-                    .Select((a, b) => new
+                var mappingValues = dictService.Context.Queryable<SysDictType, SysDictData>((u, a) =>
+                    new JoinQueryInfos(JoinType.Inner, u.Id == a.DictTypeId))
+                    .Where(u => u.Code == attrs.TypeCode)
+                    .Where((u, a) => u.Status == StatusEnum.Enable && a.Status == StatusEnum.Enable)
+                    .Select((u, a) => new
                     {
-                        Label = b.Value,
-                        Value = b.Code
+                        Label = a.Value,
+                        Value = a.Code
                     }).ToList()
-                    .ToDictionary(m => m.Label, m => m.Value.ParseTo(targetProp.PropertyType));
+                    .ToDictionary(u => u.Label, u => u.Value.ParseTo(targetProp.PropertyType));
                 propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>(mappingValues, propertyInfo, targetProp));
             }
             else
@@ -317,25 +318,25 @@ public static class CommonUtil
         // 整理导入对象的属性名称,<字典数据,原属性信息,目标属性信息>
         var propMappings = new Dictionary<string, Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>>();
 
-        var dictService = App.GetService<SqlSugarRepository<SysDictData>>();
+        var dictService = App.GetRequiredService<SqlSugarRepository<SysDictData>>();
         var targetProps = typeof(TTarget).GetProperties().ToList();
-        var sourceProps = typeof(TSource).GetProperties().ToDictionary(m => m.Name);
+        var sourceProps = typeof(TSource).GetProperties().ToDictionary(u => u.Name);
         foreach (var propertyInfo in targetProps)
         {
             var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
             if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
             {
                 var targetProp = sourceProps[attrs.TargetPropName];
-                var mappingValues = dictService.Context.Queryable<SysDictType, SysDictData>((a, b) =>
-                    new JoinQueryInfos(JoinType.Inner, a.Id == b.DictTypeId))
-                    .Where(a => a.Code == attrs.TypeCode)
-                    .Where((a, b) => a.Status == StatusEnum.Enable && b.Status == StatusEnum.Enable)
-                    .Select((a, b) => new
+                var mappingValues = dictService.Context.Queryable<SysDictType, SysDictData>((u, a) =>
+                    new JoinQueryInfos(JoinType.Inner, u.Id == a.DictTypeId))
+                    .Where(u => u.Code == attrs.TypeCode)
+                    .Where((u, a) => u.Status == StatusEnum.Enable && a.Status == StatusEnum.Enable)
+                    .Select((u, a) => new
                     {
-                        Label = b.Value,
-                        Value = b.Code
+                        Label = a.Value,
+                        Value = a.Code
                     }).ToList()
-                    .ToDictionary(m => m.Value.ParseTo(targetProp.PropertyType), m => m.Label);
+                    .ToDictionary(u => u.Value.ParseTo(targetProp.PropertyType), u => u.Label);
                 propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>(mappingValues, targetProp, propertyInfo));
             }
             else
@@ -369,4 +370,47 @@ public static class CommonUtil
 
         return propMappings;
     }
+
+    /// <summary>
+    /// 解析IP地址
+    /// </summary>
+    /// <param name="ip"></param>
+    /// <returns></returns>
+    public static (string ipLocation, double? longitude, double? latitude) GetIpAddress(string ip)
+    {
+        try
+        {
+            var ipInfo = IpTool.SearchWithI18N(ip); // 国际化查询,默认中文 中文zh-CN、英文en
+            var addressList = new List<string>() { ipInfo.Country, ipInfo.Province, ipInfo.City, ipInfo.NetworkOperator };
+            return (string.Join(" ", addressList.Where(u => u != "0" && !string.IsNullOrWhiteSpace(u)).ToList()), ipInfo.Longitude, ipInfo.Latitude); // 去掉0及空并用空格连接
+        }
+        catch
+        {
+            // 不做处理
+        }
+        return ("未知", 0, 0);
+    }
+
+    /// <summary>
+    /// 获取客户端设备信息(操作系统+浏览器)
+    /// </summary>
+    /// <param name="userAgent"></param>
+    /// <returns></returns>
+    public static string GetClientDeviceInfo(string userAgent)
+    {
+        try
+        {
+            if (userAgent != null)
+            {
+                var client = Parser.GetDefault().Parse(userAgent);
+                if (client.Device.IsSpider)
+                    return "爬虫";
+                return $"{client.OS.Family} {client.OS.Major} {client.OS.Minor}" +
+                    $"|{client.UA.Family} {client.UA.Major}.{client.UA.Minor} / {client.Device.Family}";
+            }
+        }
+        catch
+        { }
+        return "未知";
+    }
 }

+ 10 - 3
Admin.NET/Admin.NET.Core/Util/ComputerUtil.cs

@@ -148,9 +148,16 @@ public static class ComputerUtil
     public static string GetIpFromOnline()
     {
         var url = "https://www.ip.cn/api/index?ip&type=0";
-        var str = url.GetAsStringAsync().GetAwaiter().GetResult();
-        var resp = JSON.Deserialize<IpCnResp>(str);
-        return resp.Ip + " " + resp.Address;
+        try
+        {
+            var str = url.GetAsStringAsync().GetAwaiter().GetResult();
+            var resp = JSON.Deserialize<IpCnResp>(str);
+            return resp.Ip + " " + resp.Address;
+        }
+        catch
+        {
+            return "";
+        }
     }
 
     public static bool IsUnix()

+ 7 - 6
Admin.NET/Admin.NET.Web.Core/Handlers/JwtHandler.cs

@@ -92,16 +92,17 @@ namespace Admin.NET.Web.Core
                 ? httpContext.Request.Path.Value[5..].Replace("/", ":")
                 : httpContext.Request.Path.Value[1..].Replace("/", ":");
 
-            var sysMenuService = App.GetRequiredService<SysMenuService>();
+            var serviceScope = httpContext.RequestServices.CreateScope();
+            var sysMenuService = serviceScope.ServiceProvider.GetRequiredService<SysMenuService>();
+
             // 获取用户拥有按钮权限集合
             var ownBtnPermList = await sysMenuService.GetOwnBtnPermList();
+            if (ownBtnPermList.Exists(u => routeName.Equals(u, StringComparison.CurrentCultureIgnoreCase)))
+                return true;
+
             // 获取系统所有按钮权限集合
             var allBtnPermList = await sysMenuService.GetAllBtnPermList();
-
-            // 已拥有该按钮权限或者所有按钮集合里面不存在
-            var exist1 = ownBtnPermList.Exists(u => routeName.Equals(u, StringComparison.CurrentCultureIgnoreCase));
-            var exist2 = allBtnPermList.TrueForAll(u => !routeName.Equals(u, StringComparison.CurrentCultureIgnoreCase));
-            return exist1 || exist2;
+            return allBtnPermList.TrueForAll(u => !routeName.Equals(u, StringComparison.CurrentCultureIgnoreCase));
         }
     }
 }

+ 36 - 9
Admin.NET/Admin.NET.Web.Core/Startup.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -8,10 +8,12 @@ using Admin.NET.Core;
 using Admin.NET.Core.Service;
 using AspNetCoreRateLimit;
 using Furion;
+using Furion.Logging;
 using Furion.SpecificationDocument;
 using Furion.VirtualFileServer;
 using IGeekFan.AspNetCore.Knife4jUI;
 using IPTools.Core;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
@@ -22,6 +24,7 @@ using Newtonsoft.Json;
 using OnceMi.AspNetCore.OSS;
 using SixLabors.ImageSharp.Web.DependencyInjection;
 using System;
+using System.Threading.Tasks;
 
 namespace Admin.NET.Web.Core;
 
@@ -37,12 +40,25 @@ public class Startup : AppStartup
         // SqlSugar
         services.AddSqlSugar();
         // JWT
-        services.AddJwt<JwtHandler>(enableGlobalAuthorize: true)
-            // 添加 Signature 身份验证
-            .AddSignatureAuthentication(options =>
+        services.AddJwt<JwtHandler>(enableGlobalAuthorize: true, jwtBearerConfigure: options =>
+        {
+            // 实现 JWT 身份验证过程控制
+            options.Events = new JwtBearerEvents
             {
-                options.Events = SysOpenAccessService.GetSignatureAuthenticationEventImpl();
-            });
+                OnMessageReceived = context =>
+                {
+                    var httpContext = context.HttpContext;
+                    // 若请求 Url 包含 token 参数,则设置 Token 值
+                    if (httpContext.Request.Query.ContainsKey("token"))
+                        context.Token = httpContext.Request.Query["token"];
+                    return Task.CompletedTask;
+                }
+            };
+        }).AddSignatureAuthentication(options =>  // 添加 Signature 身份验证
+        {
+            options.Events = SysOpenAccessService.GetSignatureAuthenticationEventImpl();
+        });
+
         // 允许跨域
         services.AddCorsAccessor();
         // 远程请求
@@ -108,15 +124,23 @@ public class Startup : AppStartup
             options.LogEnabled = false;
             // 事件执行器(失败重试)
             options.AddExecutor<RetryEventHandlerExecutor>();
+            // 事件执行器(重试后依然处理未处理异常的处理器)
+            options.UnobservedTaskExceptionHandler = (obj, args) =>
+            {
+                if (args.Exception?.Message != null)
+                    Log.Error($"EeventBus 有未处理异常 :{args.Exception?.Message} ", args.Exception);
+            };
+            // 事件执行器-监视器(每一次处理都会进入)
+            options.AddMonitor<EventHandlerMonitor>();
 
             #region Redis消息队列
 
             //// 替换事件源存储器
             //options.ReplaceStorer(serviceProvider =>
             //{
-            //    var redisCache = serviceProvider.GetRequiredService<ICache>();
+            //    var cacheProvider = serviceProvider.GetRequiredService<NewLife.Caching.ICacheProvider>();
             //    // 创建默认内存通道事件源对象,可自定义队列路由key,如:adminnet
-            //    return new RedisEventSourceStorer(redisCache, "adminnet", 3000);
+            //    return new RedisEventSourceStorer(cacheProvider, "adminnet", 3000);
             //});
 
             #endregion Redis消息队列
@@ -153,8 +177,11 @@ public class Startup : AppStartup
         services.AddViewEngine();
 
         // 即时通讯
-        services.AddSignalR(SetNewtonsoftJsonSetting);
         //services.AddSingleton<IUserIdProvider, UserIdProvider>();
+        services.AddSignalR(options =>
+        {
+            options.KeepAliveInterval = TimeSpan.FromSeconds(5);
+        }).AddNewtonsoftJsonProtocol(options => SetNewtonsoftJsonSetting(options.PayloadSerializerSettings));
 
         // 系统日志
         services.AddLoggingSetup();

+ 2 - 2
Admin.NET/Admin.NET.Web.Entry/Program.cs

@@ -16,8 +16,8 @@ public class WebComponent : IWebComponent
             return !new[] { "Microsoft.Hosting", "Microsoft.AspNetCore" }.Any(u => category.StartsWith(u)) && logLevel >= LogLevel.Information;
         });
 
-        // 设置接口超时时间和上传大小
-        builder.Configuration.Get<WebHostBuilder>().ConfigureKestrel(u =>
+        // 设置接口超时时间和上传大小-Kestrel
+        builder.WebHost.ConfigureKestrel(u =>
         {
             u.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(30);
             u.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(30);

+ 27 - 31
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/Service.cs.vm

@@ -23,22 +23,12 @@ namespace @Model.NameSpace;
 /// @(@Model.BusName)服务
 /// </summary>
 [ApiDescriptionSettings(@(@Model.ProjectLastName)Const.GroupName, Order = 100)]
-[DisplayName("@(@Model.BusName)服务")]
 public class @(@Model.ClassName)Service : IDynamicApiController, ITransient
 {
-    private SqlSugarRepository<@(@Model.ClassName)> @(@Model.LowerClassName)Rep = null;
-
-    public @(@Model.ClassName)Service()
+    private readonly SqlSugarRepository<@(@Model.ClassName)> _@(@Model.LowerClassName)Rep;
+    public @(@Model.ClassName)Service(SqlSugarRepository<@(@Model.ClassName)> @(@Model.LowerClassName)Rep)
     {
-    }
-
-    public SqlSugarRepository<@(@Model.ClassName)> @(@Model.ClassName)Rep
-    {
-        get
-        {
-            @(@Model.LowerClassName)Rep ??= App.GetRequiredService<SqlSugarRepository<@(@Model.ClassName)>>();
-            return @(@Model.LowerClassName)Rep;
-        }
+        _@(@Model.LowerClassName)Rep = @(@Model.LowerClassName)Rep;
     }
 
     /// <summary>
@@ -46,14 +36,15 @@ public class @(@Model.ClassName)Service : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
-    [ApiDescriptionSettings(Name = "Page"), HttpPost]
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Page")]
     [DisplayName("分页查询@(@Model.BusName)")]
     public async Task<SqlSugarPagedList<@(@Model.ClassName)Output>> Page(@(@Model.ClassName)Input input)
     {
 @if (haveLikeCdt) {
 		@:input.SearchKey = input.SearchKey?.Trim();
 }
-        var query = @(@Model.ClassName)Rep.AsQueryable()
+        var query = _@(@Model.LowerClassName)Rep.AsQueryable()
 @{string conditionFlag = "";}
 @if (haveLikeCdt) {
             @:.WhereIF(!string.IsNullOrEmpty(input.SearchKey), u =>
@@ -120,7 +111,7 @@ if (@column.QueryWhether == "Y"){
 } else {
             @:.Select<@(@Model.ClassName)Output>();
 }
-        return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
+		return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
     }
 
     /// <summary>
@@ -128,12 +119,13 @@ if (@column.QueryWhether == "Y"){
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
-    [ApiDescriptionSettings(Name = "Add"), HttpPost]
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Add")]
     [DisplayName("增加@(@Model.BusName)")]
     public async Task<long> Add(Add@(@Model.ClassName)Input input)
     {
         var entity = input.Adapt<@(@Model.ClassName)>();
-        await @(@Model.ClassName)Rep.InsertAsync(entity);
+        await _@(@Model.LowerClassName)Rep.InsertAsync(entity);
         return entity.Id;
     }
 
@@ -142,17 +134,18 @@ if (@column.QueryWhether == "Y"){
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
-    [ApiDescriptionSettings(Name = "Delete"), HttpPost]
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Delete")]
     [DisplayName("删除@(@Model.BusName)")]
     public async Task Delete(Delete@(@Model.ClassName)Input input)
     {
 @foreach (var column in Model.TableField){
 if (@column.ColumnKey == "True"){
-        @:var entity = await @(@Model.ClassName)Rep.GetFirstAsync(u => u.@(@column.PropertyName) == input.@(@column.PropertyName)) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+        @:var entity = await _@(@Model.LowerClassName)Rep.GetFirstAsync(u => u.@(@column.PropertyName) == input.@(@column.PropertyName)) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
 }
 }
-        await @(@Model.ClassName)Rep.FakeDeleteAsync(entity);   //假删除
-        //await @(@Model.ClassName)Rep.DeleteAsync(entity);   //真删除
+        await _@(@Model.LowerClassName)Rep.FakeDeleteAsync(entity);   //假删除
+        //await _@(@Model.LowerClassName)Rep.DeleteAsync(entity);   //真删除
     }
 
     /// <summary>
@@ -160,12 +153,13 @@ if (@column.ColumnKey == "True"){
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
-    [ApiDescriptionSettings(Name = "Update"), HttpPost]
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Update")]
     [DisplayName("更新@(@Model.BusName)")]
     public async Task Update(Update@(@Model.ClassName)Input input)
     {
         var entity = input.Adapt<@(@Model.ClassName)>();
-        await @(@Model.ClassName)Rep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
+        await _@(@Model.LowerClassName)Rep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
     }
 
     /// <summary>
@@ -173,13 +167,14 @@ if (@column.ColumnKey == "True"){
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
-    [ApiDescriptionSettings(Name = "Detail"), HttpGet]
+    [HttpGet]
+    [ApiDescriptionSettings(Name = "Detail")]
     [DisplayName("获取@(@Model.BusName)")]
     public async Task<@(@Model.ClassName)> Detail([FromQuery] QueryById@(@Model.ClassName)Input input)
     {
 @foreach (var column in Model.TableField){
 if (@column.ColumnKey == "True"){
-        @:return await @(@Model.ClassName)Rep.GetFirstAsync(u => u.@(@column.PropertyName) == input.@(@column.PropertyName));
+        @:return await _@(@Model.LowerClassName)Rep.GetFirstAsync(u => u.@(@column.PropertyName) == input.@(@column.PropertyName));
 }   
 }            
     }
@@ -189,11 +184,12 @@ if (@column.ColumnKey == "True"){
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
-    [ApiDescriptionSettings(Name = "List"), HttpGet]
+    [HttpGet]
+    [ApiDescriptionSettings(Name = "List")]
     [DisplayName("获取@(@Model.BusName)列表")]
     public async Task<List<@(@Model.ClassName)Output>> List([FromQuery] @(@Model.ClassName)Input input)
     {
-        return await @(@Model.ClassName)Rep.AsQueryable().Select<@(@Model.ClassName)Output>().ToListAsync();
+        return await _@(@Model.LowerClassName)Rep.AsQueryable().Select<@(@Model.ClassName)Output>().ToListAsync();
     }
 
 @foreach (var column in Model.TableField){
@@ -201,13 +197,12 @@ if(@column.EffectType == "fk" && (@column.WhetherAddUpdate == "Y" || column.Quer
     @:/// <summary>
     @:/// 获取@(@column.ColumnComment)列表
     @:/// </summary>
-    @:/// <param name="input"></param>
     @:/// <returns></returns>
     @:[ApiDescriptionSettings(Name = "@(@column.FkEntityName)@(@column.PropertyName)Dropdown"), HttpGet]
     @:[DisplayName("获取@(@column.ColumnComment)列表")]
     @:public async Task<dynamic> @(@column.FkEntityName)@(@column.PropertyName)Dropdown()
     @:{
-        @:return await @(@Model.ClassName)Rep.Context.Queryable<@(@column.FkEntityName)>()
+        @:return await _@(@Model.LowerClassName)Rep.Context.Queryable<@(@column.FkEntityName)>()
                 @:.Select(u => new
                 @:{
                     @:Label = u.@(@column.FkColumnName),
@@ -240,9 +235,10 @@ if(@column.EffectType == "Upload"){
 if(@column.EffectType == "ApiTreeSelect" && !definedObjects.ContainsKey("@(@column.FkEntityName)Tree")){
     @{definedObjects.Add("@(@column.FkEntityName)Tree", 1);}
     @:[HttpGet("@(@column.FkEntityName)Tree")]
+    @:[DisplayName("获取@(@column.FkEntityName)Tree")]
     @:public async Task<dynamic> @(@column.FkEntityName)Tree()
     @:{
-        @:return await @(@Model.ClassName)Rep.Context.Queryable<@(@column.FkEntityName)>().ToTreeAsync(u => u.Children, u => u.@(@column.PidColumn), 0);
+        @:return await _@(@Model.LowerClassName)Rep.Context.Queryable<@(@column.FkEntityName)>().ToTreeAsync(u => u.Children, u => u.@(@column.PidColumn), 0);
     @:}
 }
 }

+ 24 - 26
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/editDialog.vue.vm

@@ -78,7 +78,7 @@
 					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 						@:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
 							@:<el-select clearable v-model="ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
-								@:<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')"  :key="index" :value="item.code" :label="`[${item.code}] ${item.value}`"></el-option>
+								@:<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')"  :key="index" :value="@(@column.NetType.StartsWith("string") ? "item.code" : "Number(item.code)")" :label="`[${item.code}] ${item.value}`"></el-option>
 								@:
 							</el-select>
 							@:
@@ -89,7 +89,7 @@
 					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 						@:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
 							@:<el-select clearable v-model="ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
-								@:<el-option v-for="(item,index) in getConstType('@column.DictTypeCode')" :key="index" :label="item.name" :value="item.code">{{ item.name }}</el-option>
+								@:<el-option v-for="(item,index) in getConstType('@column.DictTypeCode')" :key="index" :label="item.name" :value="@(@column.NetType.StartsWith("string") ? "item.code" : "Number(item.code)")">{{ item.name }}</el-option>
 								@:
 							</el-select>
 							@:
@@ -135,7 +135,7 @@
 					@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 						@:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
 							@:<el-select clearable v-model="ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
-								@:<el-option v-for="(item,index) in  getEnum@(@column.PropertyName)Data" :key="index" :value="item.value" :label="`[${item.value}] ${item.describe}`"></el-option>
+								@:<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')" :key="index" :value="@(@column.NetType.StartsWith("string") ? "item.code" : "Number(item.code)")" :label="`${item.name} (${item.code}) [${item.value}]`"></el-option>
 								@:
 							</el-select>
 							@:
@@ -159,7 +159,7 @@
 		</el-dialog>
 	</div>
 </template>
-<style scoped>
+<style lang="scss" scoped>
 :deep(.el-select),
 :deep(.el-input-number) {
 	width: 100%;
@@ -167,19 +167,27 @@
 </style>
 <script lang="ts" setup>
 	import { ref,onMounted } from "vue";
-	import { getDictDataItem as di, getDictDataList as dl } from '/@@/utils/dict-utils';
 	import { ElMessage } from "element-plus";
 	import type { FormRules } from "element-plus";
 @if(@Model.TableField.Any(x=>x.EffectType == "ConstSelector")){
 	@:import { getConstType } from "/@@/utils/constHelper";
-	}
+} 
+@if(@Model.TableField.Any(x=>x.EffectType == "Select") || @Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
+	@:import { getDictDataItem as di, getDictDataList as dl } from '/@@/utils/dict-utils';
+}
+@if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
+	@:import { getDictLabelByVal as dv } from '/@@/utils/dict-utils';
+}
+@if(@Model.TableField.Any(x=>x.EffectType == "DatePicker")){
+  	@:import { formatDate } from '/@@/utils/formatTime';
+}
 @if(@Model.TableField.Any(x=>x.EffectType == "Upload")){
     @:import { Plus } from "@@element-plus/icons-vue";
     @:import { UploadRequestOptions } from "element-plus";
     @:import {@string.Join(",",Model.TableField.Where(x=>x.EffectType == "Upload").Select(x=>"upload"+x.PropertyName).ToList())} from '/@@/api/@(@Model.PagePath)/@(@Model.LowerClassName)';
 }
 	import { add@(@Model.ClassName), update@(@Model.ClassName), detail@(@Model.ClassName) } from "/@@/api/@(@Model.PagePath)/@(@Model.LowerClassName)";
-	@foreach (var column in Model.TableField){
+@foreach (var column in Model.TableField){
 	if(@column.EffectType == "ApiTreeSelect" && !definedObjects.ContainsKey("import__@(@column.FkEntityName)Tree")){
 	@{definedObjects.Add("import__@(@column.FkEntityName)Tree", 1);}
 	@:import { get@(@column.FkEntityName)Tree } from '/@@/api/@(@Model.PagePath)/@(@Model.LowerClassName)';
@@ -187,17 +195,12 @@
 	if(@column.EffectType == "fk" && @column.WhetherAddUpdate == "Y"){
 	@:import { get@(@column.FkEntityName)@(@column.PropertyName)Dropdown } from '/@@/api/@(@Model.PagePath)/@(@Model.LowerClassName)';
 	}
-	}
-	@if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
+}
+@if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
 	@:import { getAPI } from '/@@/utils/axios-utils';
 	@:import { SysEnumApi } from '/@@/api-services/api';
-	}
+}
 
-	@foreach (var column in Model.TableField){
-	if(@column.EffectType == "EnumSelector"){
-	@:const getEnum@(@column.PropertyName)Data = ref<any>([]);
-	}
-	}
 	//父级传递来的参数
 	var props = defineProps({
 		title: {
@@ -220,7 +223,12 @@
 		@:@column.LowerPropertyName: [{required: true, message: '请选择@(@column.ColumnComment)!', trigger: 'change',},],
 		}
     }
-  }
+}
+	});
+
+	// 页面加载时
+	onMounted(() => {
+
 	});
 
 	// 打开弹窗
@@ -312,16 +320,6 @@
 		}
 	}
 
-
-	// 页面加载时
-	onMounted(async () => {
-		@foreach (var column in Model.TableField){
-		if(@column.EffectType == "EnumSelector"){
-			@:getEnum@(@column.PropertyName)Data.value = (await getAPI(SysEnumApi).apiSysEnumEnumDataListGet('@(@column.DictTypeCode)')).data.result ?? [];
-		}
-		}
-	});
-
 	//将属性或者函数暴露给父组件
 	defineExpose({ openDialog });
 </script>

+ 17 - 24
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/index.vue.vm

@@ -1,4 +1,4 @@
-@{
+@{
   var pkField = Model.TableField.Where(c => c.ColumnKey == "True").FirstOrDefault();
   string pkFieldName = null;
   if(pkField != null && !string.IsNullOrEmpty(pkField.PropertyName))
@@ -47,7 +47,7 @@
             }else if(@column.EffectType == "fk"){
             @:<el-form-item label="@column.ColumnComment">
               @:<el-select clearable="" filterable="" v-model="queryParams.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
-                @:<el-option v-for="(item,index) in  @LowerFirstLetter(@column.FkEntityName)@(@column.PropertyName)DropdownList" :key="index" :value="item.value" :label="item.label" />
+                @:<el-option v-for="(item,index) in @LowerFirstLetter(@column.FkEntityName)@(@column.PropertyName)DropdownList" :key="index" :value="item.value" :label="item.label" />
                 @:
               </el-select>
               @:
@@ -63,7 +63,7 @@
             }else if(@column.EffectType == "EnumSelector"){
             @:<el-form-item label="@column.ColumnComment">
               @:<el-select clearable="" v-model="queryParams.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
-                @:<el-option v-for="(item,index) in getEnum@(@column.PropertyName)Data_Index" :key="index" :value="item.value" :label="`[${item.value}] ${item.describe}`" />
+                @:<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')" :key="index" :value="item.value" :label="`${item.name} (${item.code}) [${item.value}] `" />
                 @:
               </el-select>
               @:
@@ -83,9 +83,9 @@
             } 
       }
           @<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10">
-            @<el-form-item>
+            @<el-form-item @(Model.QueryWhetherList.Count > 0?"":"label-width=\"0px\"")>
               @<el-button-group style="display: flex; align-items: center;">
-                @<el-button type="primary"  icon="ele-Search" @@click="handleQuery" v-auth="'@(@Model.LowerClassName):page'"> 查询 </el-button>
+                @<el-button type="primary"  icon="ele-Search" @@click="handleQuery" v-auth="'@(@Model.LowerClassName):page'"> @(Model.QueryWhetherList.Count > 0?"查询":"刷新") </el-button>
                    @if(Model.QueryWhetherList.Count > 0){
                       @:<el-button icon="ele-Refresh" @@click="() => queryParams = {}"> 重置 </el-button>
                         @if(haveLikeCdt){
@@ -165,7 +165,7 @@
         else if(@column.EffectType == "EnumSelector"){
           @:<el-table-column prop="@column.LowerPropertyName" label="@column.ColumnComment" @(column.WhetherSortable == "Y" ? "sortable='custom'" : "") show-overflow-tooltip="" >
             @:<template #default="scope">
-              @:<el-tag>{{ getEnumDesc(scope.row.@column.LowerPropertyName, getEnum@(@column.PropertyName)Data_Index)}}</el-tag>
+              @:<el-tag :type="dv('@(@column.DictTypeCode)', scope.row.@(@column.LowerPropertyName))?.tagType"> {{dv('@(@column.DictTypeCode)', scope.row.@column.LowerPropertyName)?.name}}</el-tag>
             @:</template>
           @:</el-table-column>
         }
@@ -192,7 +192,7 @@
 				v-model:page-size="tableParams.pageSize"
 				:total="tableParams.total"
 				:page-sizes="[10, 20, 50, 100, 200, 500]"
-				small=""
+				size="small"
 				background=""
 				@@size-change="handleSizeChange"
 				@@current-change="handleCurrentChange"
@@ -215,11 +215,19 @@
   import { ref } from "vue";
   import { ElMessageBox, ElMessage } from "element-plus";
   import { auth } from '/@@/utils/authFunction';
+
   @if(@Model.TableField.Any(x=>x.EffectType == "ConstSelector")){
   @:import { codeToName, getConstType } from "/@@/utils/constHelper";
   } 
-  import { getDictDataItem as di, getDictDataList as dl } from '/@@/utils/dict-utils';
-  import { formatDate } from '/@@/utils/formatTime';
+  @if(@Model.TableField.Any(x=>x.EffectType == "Select") || @Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
+  @:import { getDictDataItem as di, getDictDataList as dl } from '/@@/utils/dict-utils';
+  }
+  @if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
+  @:import { getDictLabelByVal as dv } from '/@@/utils/dict-utils';
+  }
+  @if(@Model.TableField.Any(x=>x.EffectType == "DatePicker")){
+  @:import { formatDate } from '/@@/utils/formatTime';
+  }
 
   @if(@Model.PrintType == "custom"){
   @:// 推荐设置操作 width 为 200
@@ -242,15 +250,6 @@
   @:import commonFunction from '/@@/utils/commonFunction';
 	}
 
-  @foreach (var column in Model.QueryWhetherList){
-  if(@column.EffectType == "EnumSelector"){
-  @:const getEnum@(@column.PropertyName)Data_Index = ref<any>([]);
-  }
-  }
-
-  @if(@Model.QueryWhetherList.Any(x=>x.EffectType == "EnumSelector")){
-	@:const { getEnumDesc } = commonFunction();
-	}
   @if(haveLikeCdt){
   @:const showAdvanceQueryUI = ref(false);
   }else {
@@ -274,7 +273,6 @@
   const changeAdvanceQueryUI = () => {
     showAdvanceQueryUI.value = !showAdvanceQueryUI.value;
   }
-  
 
   // 查询操作
   const handleQuery = async () => {
@@ -283,11 +281,6 @@
     tableData.value = res.data.result?.items ?? [];
     tableParams.value.total = res.data.result?.total;
     loading.value = false;
-  @foreach (var column in Model.QueryWhetherList){
-  if(@column.EffectType == "EnumSelector"){
-    @:getEnum@(@column.PropertyName)Data_Index.value = (await getAPI(SysEnumApi).apiSysEnumEnumDataListGet('@(@column.DictTypeCode)')).data.result ?? [];
-  }
-  }
   };
 
   // 列排序

+ 1 - 1
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Middleware/ApprovalFlowMiddleware.cs

@@ -39,7 +39,7 @@ public class ApprovalFlowMiddleware
 
     public async Task Invoke(HttpContext context)
     {
-        //await App.GetService<SysApprovalService>().MatchApproval(context);
+        // await App.GetRequiredService<SysApprovalService>().MatchApproval(context);
 
         await _next.Invoke(context);
     }

+ 13 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Const/DingTalkConst.cs

@@ -31,4 +31,17 @@ public class DingTalkConst
     /// 工号
     /// </summary>
     public const string JobNumberField = "sys00-jobNumber";
+
+    /// <summary>
+    /// 主部门Id
+    /// </summary>
+    public const string DeptId = "sys00-mainDeptId";
+    /// <summary>
+    /// 主部门
+    /// </summary>
+    public const string Dept = "sys00-mainDept";
+    /// <summary>
+    /// 职位
+    /// </summary>
+    public const string Position = "sys00-position";
 }

+ 19 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkUser.cs

@@ -73,4 +73,23 @@ public class DingTalkUser : EntityBase
     [SugarColumn(ColumnDescription = "工号", Length = 16)]
     [MaxLength(16)]
     public string? JobNumber { get; set; }
+    /// <summary>
+    /// 主部门Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "主部门Id", Length = 16)]
+    [MaxLength(16)]
+    public string? DeptId { get; set; }
+    /// <summary>
+    /// 主部门
+    /// </summary>
+    [SugarColumn(ColumnDescription = "主部门", Length = 16)]
+    [MaxLength(16)]
+    public string? Dept { get; set; }
+    /// <summary>
+    /// 职位
+    /// </summary>
+    [SugarColumn(ColumnDescription = "职位", Length = 16)]
+    [MaxLength(16)]
+    public string? Position { get; set; }
+
 }

+ 13 - 1
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Job/SyncDingTalkUserJob.cs

@@ -95,6 +95,9 @@ public class SyncDingTalkUserJob : IJob
             Name = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.NameField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
             Mobile = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.MobileField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
             JobNumber = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.JobNumberField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
+            DeptId = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.DeptId).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
+            Dept = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.Dept).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
+            Position = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.Position).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
         }).ToList();
         if (iUser.Count > 0)
         {
@@ -109,6 +112,9 @@ public class SyncDingTalkUserJob : IJob
             Name = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.NameField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
             Mobile = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.MobileField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
             JobNumber = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.JobNumberField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
+            DeptId = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.DeptId).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
+            Dept = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.Dept).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
+            Position = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.Position).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
         }).ToList();
         if (uUser.Count > 0)
         {
@@ -118,6 +124,9 @@ public class SyncDingTalkUserJob : IJob
                 u.Name,
                 u.Mobile,
                 u.JobNumber,
+                u.DeptId,
+                u.Dept,
+                u.Position,
                 u.UpdateTime,
                 u.UpdateUserName,
                 u.UpdateUserId,
@@ -137,7 +146,10 @@ public class SyncDingTalkUserJob : IJob
             {
                 u.Id,
                 u.JobNumber,
-                u.Mobile
+                u.Mobile,
+                u.DeptId,
+                u.Dept,
+                u.Position
             }).ToListAsync();
         var uSysDingTalkUser = sysDingTalkUser.Select(u => new DingTalkUser
         {

+ 2 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkCurrentEmployeesRosterListInput.cs

@@ -25,5 +25,7 @@ public class GetDingTalkCurrentEmployeesRosterListInput
     /// <summary>
     /// 应用的AgentId
     /// </summary>
+    [Newtonsoft.Json.JsonProperty("agentid")]
+    [System.Text.Json.Serialization.JsonPropertyName("agentid")]
     public string AgentId { get; set; }
 }

+ 2 - 1
Admin.NET/Plugins/Admin.NET.Plugin.GoView/Entity/GoViewPro.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -10,6 +10,7 @@ namespace Admin.NET.Plugin.GoView;
 /// GoView 项目表
 /// </summary>
 [SugarTable(null, "GoView 项目表")]
+[SysTable]
 public class GoViewPro : EntityTenant
 {
     /// <summary>

+ 8 - 1
Admin.NET/Plugins/Admin.NET.Plugin.GoView/Entity/GoViewProData.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -10,6 +10,7 @@ namespace Admin.NET.Plugin.GoView;
 /// GoView 项目数据表
 /// </summary>
 [SugarTable(null, "GoView 项目数据表")]
+[SysTable]
 public class GoViewProData : EntityTenant
 {
     /// <summary>
@@ -23,4 +24,10 @@ public class GoViewProData : EntityTenant
     /// </summary>
     [SugarColumn(ColumnDescription = "预览图片", ColumnDataType = StaticConfig.CodeFirst_BigString)]
     public string? IndexImageData { get; set; }
+
+    /// <summary>
+    /// 背景图片
+    /// </summary>
+    [SugarColumn(ColumnDescription = "背景图片", ColumnDataType = StaticConfig.CodeFirst_BigString)]
+    public string? BackGroundImageData { get; set; }
 }

+ 6 - 1
Admin.NET/Plugins/Admin.NET.Plugin.GoView/Service/GoViewPro/Dto/GoViewProOutput.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -36,6 +36,11 @@ public class GoViewProItemOutput
     /// </summary>
     public string IndexImage { get; set; }
 
+    /// <summary>
+    /// 背景图片url
+    /// </summary>
+    public string BackGroundImage { get; set; }
+
     /// <summary>
     /// 创建者Id
     /// </summary>

+ 72 - 1
Admin.NET/Plugins/Admin.NET.Plugin.GoView/Service/GoViewPro/GoViewProService.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -249,4 +249,75 @@ public class GoViewProService : IDynamicApiController
         var bytes = Convert.FromBase64String(projectData.IndexImageData);
         return new FileStreamResult(new MemoryStream(bytes), "image/png");
     }
+
+    /// <summary>
+    /// 上传背景图
+    /// </summary>
+    [DisplayName("上传背景图")]
+    public async Task<GoViewProUploadOutput> UploadBackGround(IFormFile @object)
+    {
+        // 文件名格式示例 13414795568325_index_preview.png
+        var fileNameSplit = @object.FileName.Split('_');
+        var idStr = fileNameSplit[0];
+        if (!long.TryParse(idStr, out var id)) return new GoViewProUploadOutput();
+
+        // 将预览图转换成 Base64
+        var ms = new MemoryStream();
+        await @object.CopyToAsync(ms);
+        var base64Image = Convert.ToBase64String(ms.ToArray());
+
+        // 保存
+        if (await _goViewProDataRep.IsAnyAsync(u => u.Id == id))
+        {
+            await _goViewProDataRep.AsUpdateable()
+                .SetColumns(u => new GoViewProData
+                {
+                    BackGroundImageData = base64Image
+                })
+                .Where(u => u.Id == id)
+                .ExecuteCommandAsync();
+        }
+        else
+        {
+            await _goViewProDataRep.InsertAsync(new GoViewProData
+            {
+                Id = id,
+                BackGroundImageData = base64Image,
+            });
+        }
+
+        var output = new GoViewProUploadOutput
+        {
+            Id = id,
+            BucketName = null,
+            CreateTime = null,
+            CreateUserId = null,
+            FileName = null,
+            FileSize = 0,
+            FileSuffix = "png",
+            FileUrl = $"api/goview/project/getBackGroundImage/{id}",
+            UpdateTime = null,
+            UpdateUserId = null
+        };
+
+        return output;
+    }
+
+    /// <summary>
+    /// 获取背景图
+    /// </summary>
+    /// <returns></returns>
+    [AllowAnonymous]
+    [NonUnify]
+    [ApiDescriptionSettings(Name = "GetBackGroundImage")]
+    [DisplayName("获取背景图")]
+    public async Task<IActionResult> GetBackGroundImage(long id)
+    {
+        var projectData = await _goViewProDataRep.AsQueryable().IgnoreColumns(u => u.Content).FirstAsync(u => u.Id == id);
+        if (projectData?.BackGroundImageData == null)
+            return new NoContentResult();
+
+        var bytes = Convert.FromBase64String(projectData.BackGroundImageData);
+        return new FileStreamResult(new MemoryStream(bytes), "image/png");
+    }
 }

+ 4 - 4
Admin.NET/Plugins/Admin.NET.Plugin.GoView/Service/GoViewSys/GoViewSysService.cs

@@ -1,4 +1,4 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
 //
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
 //
@@ -34,7 +34,7 @@ public class GoViewSysService : IDynamicApiController
     [DisplayName("GoView 登录")]
     public async Task<GoViewLoginOutput> Login(GoViewLoginInput input)
     {
-        _sysCacheService.Set(CommonConst.SysCaptcha, false);
+        _sysCacheService.Set($"{CacheConst.KeyConfig}{CommonConst.SysCaptcha}", false);
 
         input.Password = CryptogramUtil.SM2Encrypt(input.Password);
         var loginResult = await _sysAuthService.Login(new LoginInput()
@@ -43,7 +43,7 @@ public class GoViewSysService : IDynamicApiController
             Password = input.Password,
         });
 
-        _sysCacheService.Remove(CommonConst.SysCaptcha);
+        _sysCacheService.Remove($"{CacheConst.KeyConfig}{CommonConst.SysCaptcha}");
 
         var sysUser = await _sysUserRep.AsQueryable().ClearFilter().FirstAsync(u => u.Account.Equals(input.Username));
         return new GoViewLoginOutput()
@@ -77,7 +77,7 @@ public class GoViewSysService : IDynamicApiController
     [AllowAnonymous]
     [ApiDescriptionSettings(Name = "GetOssInfo")]
     [DisplayName("获取 OSS 上传接口")]
-    public static Task<GoViewOssUrlOutput> GetOssInfo()
+    public Task<GoViewOssUrlOutput> GetOssInfo()
     {
         return Task.FromResult(new GoViewOssUrlOutput { BucketURL = "" });
     }

+ 2 - 2
Admin.NET/Plugins/Admin.NET.Plugin.ReZero/Admin.NET.Plugin.ReZero.csproj

@@ -24,8 +24,8 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="Rezero.Api" Version="1.5.16" />
-  </ItemGroup>  
+    <PackageReference Include="Rezero.Api" Version="1.7.8" />
+  </ItemGroup>
 
   <ItemGroup>
     <ProjectReference Include="..\..\Admin.NET.Core\Admin.NET.Core.csproj" />

+ 0 - 0
Web/.prettierrc.js → Web/.prettierrc.cjs


+ 25 - 21
Web/package.json

@@ -2,6 +2,7 @@
 	"name": "admin.net",
 	"type": "module",
 	"version": "2.4.33",
+	"lastBuildTime": "2024.07.02",
 	"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
 	"author": "zuohuaijun",
 	"license": "MIT",
@@ -19,7 +20,7 @@
 		"@vue-office/docx": "^1.6.2",
 		"@vue-office/excel": "^1.7.8",
 		"@vue-office/pdf": "^2.0.2",
-		"@vueuse/core": "^10.10.0",
+		"@vueuse/core": "^10.11.0",
 		"@wangeditor/editor": "^5.1.23",
 		"@wangeditor/editor-for-vue": "^5.1.12",
 		"animate.css": "^4.1.1",
@@ -27,65 +28,68 @@
 		"axios": "^1.7.2",
 		"countup.js": "^2.8.0",
 		"cropperjs": "^1.6.2",
-		"echarts": "^5.5.0",
+		"echarts": "^5.5.1",
 		"echarts-gl": "^2.0.9",
 		"echarts-wordcloud": "^2.1.0",
-		"element-plus": "^2.7.5",
+		"element-plus": "^2.7.6",
+		"ezuikit": "^1.0.0",
+		"ezuikit-js": "^8.0.5",
 		"js-cookie": "^3.0.5",
 		"js-table2excel": "^1.1.2",
 		"jsplumb": "^2.15.6",
 		"lodash-es": "^4.17.21",
 		"mitt": "^3.0.1",
-		"monaco-editor": "^0.49.0",
+		"monaco-editor": "^0.50.0",
+		"mqtt": "^4.3.8",
 		"nprogress": "^0.2.0",
 		"pinia": "^2.1.7",
 		"print-js": "^1.6.0",
 		"push.js": "^1.0.12",
 		"qrcodejs2-fixes": "^0.0.2",
-		"qs": "^6.12.1",
-		"relation-graph": "^2.2.1",
+		"qs": "^6.12.2",
+		"relation-graph": "^2.2.2",
 		"screenfull": "^6.0.2",
 		"sm-crypto-v2": "^1.9.0",
 		"sortablejs": "^1.15.2",
 		"splitpanes": "^3.1.5",
 		"vcrontab-3": "^3.3.22",
 		"vform3-builds": "^3.0.10",
-		"vue": "^3.4.27",
+		"vue": "^3.4.31",
 		"vue-clipboard3": "^2.0.0",
 		"vue-demi": "^0.14.8",
 		"vue-grid-layout": "3.0.0-beta1",
 		"vue-i18n": "^9.13.1",
 		"vue-json-pretty": "^2.4.0",
 		"vue-plugin-hiprint": "0.0.57-beta20",
-		"vue-router": "^4.3.2",
+		"vue-router": "^4.4.0",
 		"vue-signature-pad": "^3.0.2",
 		"vue3-tree-org": "^4.2.2",
-		"vuedraggable": "4.0.3",
+		"vuedraggable": "4.1.0",
 		"xlsx-js-style": "^1.2.0"
 	},
 	"devDependencies": {
 		"@plugin-web-update-notification/vite": "^1.7.1",
 		"@types/lodash-es": "^4.17.12",
-		"@types/node": "^20.14.2",
+		"@types/node": "^20.14.9",
 		"@types/nprogress": "^0.2.3",
 		"@types/sortablejs": "^1.15.8",
-		"@typescript-eslint/eslint-plugin": "^7.12.0",
-		"@typescript-eslint/parser": "^7.12.0",
+		"@typescript-eslint/eslint-plugin": "^7.15.0",
+		"@typescript-eslint/parser": "^7.15.0",
 		"@vitejs/plugin-vue": "^5.0.5",
 		"@vitejs/plugin-vue-jsx": "^4.0.0",
-		"@vue/compiler-sfc": "^3.4.27",
-		"code-inspector-plugin": "^0.14.1",
-		"eslint": "^9.4.0",
-		"eslint-plugin-vue": "^9.26.0",
+		"@vue/compiler-sfc": "^3.4.31",
+		"code-inspector-plugin": "^0.14.2",
+		"eslint": "^9.6.0",
+		"eslint-plugin-vue": "^9.27.0",
 		"less": "^4.2.0",
-		"prettier": "^3.3.1",
+		"prettier": "^3.3.2",
 		"rollup-plugin-visualizer": "^5.12.0",
-		"sass": "^1.77.4",
+		"sass": "^1.77.6",
 		"terser": "^5.31.1",
-		"typescript": "^5.4.5",
-		"vite": "^5.2.13",
+		"typescript": "^5.5.3",
+		"vite": "^5.3.2",
 		"vite-plugin-cdn-import": "^1.0.1",
-		"vite-plugin-compression2": "^1.1.1",
+		"vite-plugin-compression2": "^1.1.2",
 		"vite-plugin-vue-setup-extend": "^0.4.0",
 		"vue-eslint-parser": "^9.4.3"
 	},

+ 0 - 1
Web/src/App.vue

@@ -118,7 +118,6 @@ const loadSysInfo = () => {
 
 			const data = res.data.result;
 			// 系统logo
-			data.sysLogo = window.__env__.VITE_API_URL + data.sysLogo;
 			themeConfig.value.logoUrl = data.sysLogo;
 			// 主标题
 			themeConfig.value.globalTitle = data.sysTitle;

+ 0 - 75
Web/src/api-services/apis/sys-auth-api.ts

@@ -501,49 +501,6 @@ export const SysAuthApiAxiosParamCreator = function (configuration?: Configurati
             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
 
-            return {
-                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
-                options: localVarRequestOptions,
-            };
-        },
-        /**
-         * 
-         * @summary 获取水印配置 🔖
-         * @param {*} [options] Override http request option.
-         * @throws {RequiredError}
-         */
-        apiSysAuthWatermarkConfigGet: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            const localVarPath = `/api/sysAuth/watermarkConfig`;
-            // use dummy base URL string because the URL constructor only accepts absolute URLs.
-            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
-            let baseOptions;
-            if (configuration) {
-                baseOptions = configuration.baseOptions;
-            }
-            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
-            const localVarHeaderParameter = {} as any;
-            const localVarQueryParameter = {} as any;
-
-            // authentication Bearer required
-            // http bearer authentication required
-            if (configuration && configuration.accessToken) {
-                const accessToken = typeof configuration.accessToken === 'function'
-                    ? await configuration.accessToken()
-                    : await configuration.accessToken;
-                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
-            }
-
-            const query = new URLSearchParams(localVarUrlObj.search);
-            for (const key in localVarQueryParameter) {
-                query.set(key, localVarQueryParameter[key]);
-            }
-            for (const key in options.params) {
-                query.set(key, options.params[key]);
-            }
-            localVarUrlObj.search = (new URLSearchParams(query)).toString();
-            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
-            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
-
             return {
                 url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
                 options: localVarRequestOptions,
@@ -694,19 +651,6 @@ export const SysAuthApiFp = function(configuration?: Configuration) {
                 return axios.request(axiosRequestArgs);
             };
         },
-        /**
-         * 
-         * @summary 获取水印配置 🔖
-         * @param {*} [options] Override http request option.
-         * @throws {RequiredError}
-         */
-        async apiSysAuthWatermarkConfigGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultObject>>> {
-            const localVarAxiosArgs = await SysAuthApiAxiosParamCreator(configuration).apiSysAuthWatermarkConfigGet(options);
-            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
-                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
-                return axios.request(axiosRequestArgs);
-            };
-        },
     }
 };
 
@@ -812,15 +756,6 @@ export const SysAuthApiFactory = function (configuration?: Configuration, basePa
         async apiSysAuthUserInfoGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultLoginUserOutput>> {
             return SysAuthApiFp(configuration).apiSysAuthUserInfoGet(options).then((request) => request(axios, basePath));
         },
-        /**
-         * 
-         * @summary 获取水印配置 🔖
-         * @param {*} [options] Override http request option.
-         * @throws {RequiredError}
-         */
-        async apiSysAuthWatermarkConfigGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultObject>> {
-            return SysAuthApiFp(configuration).apiSysAuthWatermarkConfigGet(options).then((request) => request(axios, basePath));
-        },
     };
 };
 
@@ -937,14 +872,4 @@ export class SysAuthApi extends BaseAPI {
     public async apiSysAuthUserInfoGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultLoginUserOutput>> {
         return SysAuthApiFp(this.configuration).apiSysAuthUserInfoGet(options).then((request) => request(this.axios, this.basePath));
     }
-    /**
-     * 
-     * @summary 获取水印配置 🔖
-     * @param {*} [options] Override http request option.
-     * @throws {RequiredError}
-     * @memberof SysAuthApi
-     */
-    public async apiSysAuthWatermarkConfigGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultObject>> {
-        return SysAuthApiFp(this.configuration).apiSysAuthWatermarkConfigGet(options).then((request) => request(this.axios, this.basePath));
-    }
 }

+ 100 - 8
Web/src/api-services/apis/sys-config-api.ts

@@ -23,6 +23,7 @@ import { AdminResultListSysConfig } from '../models';
 import { AdminResultObject } from '../models';
 import { AdminResultSqlSugarPagedListSysConfig } from '../models';
 import { AdminResultSysConfig } from '../models';
+import { BatchConfigInput } from '../models';
 import { DeleteConfigInput } from '../models';
 import { InfoSaveInput } from '../models';
 import { PageConfigInput } from '../models';
@@ -129,6 +130,54 @@ export const SysConfigApiAxiosParamCreator = function (configuration?: Configura
                 options: localVarRequestOptions,
             };
         },
+        /**
+         * 
+         * @summary 批量更新参数配置值
+         * @param {Array<BatchConfigInput>} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysConfigBatchUpdatePost: async (body?: Array<BatchConfigInput>, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/sysConfig/batchUpdate`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
         /**
          * 
          * @summary 删除参数配置 🔖
@@ -275,10 +324,11 @@ export const SysConfigApiAxiosParamCreator = function (configuration?: Configura
         /**
          * 
          * @summary 获取参数配置列表 🔖
+         * @param {PageConfigInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        apiSysConfigListGet: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+        apiSysConfigListPost: async (body?: PageConfigInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
             const localVarPath = `/api/sysConfig/list`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, 'https://example.com');
@@ -286,7 +336,7 @@ export const SysConfigApiAxiosParamCreator = function (configuration?: Configura
             if (configuration) {
                 baseOptions = configuration.baseOptions;
             }
-            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
             const localVarHeaderParameter = {} as any;
             const localVarQueryParameter = {} as any;
 
@@ -299,6 +349,8 @@ export const SysConfigApiAxiosParamCreator = function (configuration?: Configura
                 localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
             }
 
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
             const query = new URLSearchParams(localVarUrlObj.search);
             for (const key in localVarQueryParameter) {
                 query.set(key, localVarQueryParameter[key]);
@@ -309,6 +361,8 @@ export const SysConfigApiAxiosParamCreator = function (configuration?: Configura
             localVarUrlObj.search = (new URLSearchParams(query)).toString();
             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
 
             return {
                 url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
@@ -539,6 +593,20 @@ export const SysConfigApiFp = function(configuration?: Configuration) {
                 return axios.request(axiosRequestArgs);
             };
         },
+        /**
+         * 
+         * @summary 批量更新参数配置值
+         * @param {Array<BatchConfigInput>} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysConfigBatchUpdatePost(body?: Array<BatchConfigInput>, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysConfigApiAxiosParamCreator(configuration).apiSysConfigBatchUpdatePost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
         /**
          * 
          * @summary 删除参数配置 🔖
@@ -583,11 +651,12 @@ export const SysConfigApiFp = function(configuration?: Configuration) {
         /**
          * 
          * @summary 获取参数配置列表 🔖
+         * @param {PageConfigInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async apiSysConfigListGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultListSysConfig>>> {
-            const localVarAxiosArgs = await SysConfigApiAxiosParamCreator(configuration).apiSysConfigListGet(options);
+        async apiSysConfigListPost(body?: PageConfigInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultListSysConfig>>> {
+            const localVarAxiosArgs = await SysConfigApiAxiosParamCreator(configuration).apiSysConfigListPost(body, options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
@@ -677,6 +746,16 @@ export const SysConfigApiFactory = function (configuration?: Configuration, base
         async apiSysConfigBatchDeletePost(body?: Array<number>, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
             return SysConfigApiFp(configuration).apiSysConfigBatchDeletePost(body, options).then((request) => request(axios, basePath));
         },
+        /**
+         * 
+         * @summary 批量更新参数配置值
+         * @param {Array<BatchConfigInput>} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysConfigBatchUpdatePost(body?: Array<BatchConfigInput>, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return SysConfigApiFp(configuration).apiSysConfigBatchUpdatePost(body, options).then((request) => request(axios, basePath));
+        },
         /**
          * 
          * @summary 删除参数配置 🔖
@@ -709,11 +788,12 @@ export const SysConfigApiFactory = function (configuration?: Configuration, base
         /**
          * 
          * @summary 获取参数配置列表 🔖
+         * @param {PageConfigInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async apiSysConfigListGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultListSysConfig>> {
-            return SysConfigApiFp(configuration).apiSysConfigListGet(options).then((request) => request(axios, basePath));
+        async apiSysConfigListPost(body?: PageConfigInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultListSysConfig>> {
+            return SysConfigApiFp(configuration).apiSysConfigListPost(body, options).then((request) => request(axios, basePath));
         },
         /**
          * 
@@ -786,6 +866,17 @@ export class SysConfigApi extends BaseAPI {
     public async apiSysConfigBatchDeletePost(body?: Array<number>, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
         return SysConfigApiFp(this.configuration).apiSysConfigBatchDeletePost(body, options).then((request) => request(this.axios, this.basePath));
     }
+    /**
+     * 
+     * @summary 批量更新参数配置值
+     * @param {Array<BatchConfigInput>} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysConfigApi
+     */
+    public async apiSysConfigBatchUpdatePost(body?: Array<BatchConfigInput>, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return SysConfigApiFp(this.configuration).apiSysConfigBatchUpdatePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
     /**
      * 
      * @summary 删除参数配置 🔖
@@ -821,12 +912,13 @@ export class SysConfigApi extends BaseAPI {
     /**
      * 
      * @summary 获取参数配置列表 🔖
+     * @param {PageConfigInput} [body] 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
      * @memberof SysConfigApi
      */
-    public async apiSysConfigListGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultListSysConfig>> {
-        return SysConfigApiFp(this.configuration).apiSysConfigListGet(options).then((request) => request(this.axios, this.basePath));
+    public async apiSysConfigListPost(body?: PageConfigInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultListSysConfig>> {
+        return SysConfigApiFp(this.configuration).apiSysConfigListPost(body, options).then((request) => request(this.axios, this.basePath));
     }
     /**
      * 

+ 96 - 0
Web/src/api-services/apis/sys-sms-api.ts

@@ -72,6 +72,64 @@ export const SysSmsApiAxiosParamCreator = function (configuration?: Configuratio
                 options: localVarRequestOptions,
             };
         },
+        /**
+         * 
+         * @summary 发送短信模板
+         * @param {any} body 
+         * @param {string} phoneNumber 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysSmsAliyunSendSmsTemplatePhoneNumberPost: async (body: any, phoneNumber: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'body' is not null or undefined
+            if (body === null || body === undefined) {
+                throw new RequiredError('body','Required parameter body was null or undefined when calling apiSysSmsAliyunSendSmsTemplatePhoneNumberPost.');
+            }
+            // verify required parameter 'phoneNumber' is not null or undefined
+            if (phoneNumber === null || phoneNumber === undefined) {
+                throw new RequiredError('phoneNumber','Required parameter phoneNumber was null or undefined when calling apiSysSmsAliyunSendSmsTemplatePhoneNumberPost.');
+            }
+            const localVarPath = `/api/sysSms/aliyunSendSmsTemplate/{phoneNumber}`
+                .replace(`{${"phoneNumber"}}`, encodeURIComponent(String(phoneNumber)));
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
         /**
          * 
          * @summary 发送短信 📨
@@ -193,6 +251,21 @@ export const SysSmsApiFp = function(configuration?: Configuration) {
                 return axios.request(axiosRequestArgs);
             };
         },
+        /**
+         * 
+         * @summary 发送短信模板
+         * @param {any} body 
+         * @param {string} phoneNumber 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysSmsAliyunSendSmsTemplatePhoneNumberPost(body: any, phoneNumber: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysSmsApiAxiosParamCreator(configuration).apiSysSmsAliyunSendSmsTemplatePhoneNumberPost(body, phoneNumber, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
         /**
          * 
          * @summary 发送短信 📨
@@ -240,6 +313,17 @@ export const SysSmsApiFactory = function (configuration?: Configuration, basePat
         async apiSysSmsAliyunSendSmsPhoneNumberPost(phoneNumber: string, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
             return SysSmsApiFp(configuration).apiSysSmsAliyunSendSmsPhoneNumberPost(phoneNumber, options).then((request) => request(axios, basePath));
         },
+        /**
+         * 
+         * @summary 发送短信模板
+         * @param {any} body 
+         * @param {string} phoneNumber 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysSmsAliyunSendSmsTemplatePhoneNumberPost(body: any, phoneNumber: string, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return SysSmsApiFp(configuration).apiSysSmsAliyunSendSmsTemplatePhoneNumberPost(body, phoneNumber, options).then((request) => request(axios, basePath));
+        },
         /**
          * 
          * @summary 发送短信 📨
@@ -281,6 +365,18 @@ export class SysSmsApi extends BaseAPI {
     public async apiSysSmsAliyunSendSmsPhoneNumberPost(phoneNumber: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
         return SysSmsApiFp(this.configuration).apiSysSmsAliyunSendSmsPhoneNumberPost(phoneNumber, options).then((request) => request(this.axios, this.basePath));
     }
+    /**
+     * 
+     * @summary 发送短信模板
+     * @param {any} body 
+     * @param {string} phoneNumber 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysSmsApi
+     */
+    public async apiSysSmsAliyunSendSmsTemplatePhoneNumberPost(body: any, phoneNumber: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return SysSmsApiFp(this.configuration).apiSysSmsAliyunSendSmsTemplatePhoneNumberPost(body, phoneNumber, options).then((request) => request(this.axios, this.basePath));
+    }
     /**
      * 
      * @summary 发送短信 📨

+ 529 - 22
Web/src/api-services/apis/sys-wechat-pay-api.ts

@@ -17,10 +17,17 @@ import { Configuration } from '../configuration';
 // Some imports not used depending on template conditions
 // @ts-ignore
 import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
+import { AdminResultListSysWechatRefund } from '../models';
 import { AdminResultObject } from '../models';
+import { AdminResultSqlSugarPagedListSysWechatPay } from '../models';
 import { AdminResultSysWechatPay } from '../models';
+import { AdminResultSysWechatRefund } from '../models';
 import { AdminResultWechatPayOutput } from '../models';
+import { AdminResultWechatPayParaOutput } from '../models';
+import { AdminResultWechatPayTransactionOutput } from '../models';
+import { WechatPayPageInput } from '../models';
 import { WechatPayParaInput } from '../models';
+import { WechatPayRefundDomesticInput } from '../models';
 import { WechatPayTransactionInput } from '../models';
 /**
  * SysWechatPayApi - axios parameter creator
@@ -78,7 +85,103 @@ export const SysWechatPayApiAxiosParamCreator = function (configuration?: Config
         },
         /**
          * 
-         * @summary 微信支付成功回调(商户直连) 🔖
+         * @summary 查询退款信息列表
+         * @param {string} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysWechatPayListRefundPost: async (body?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/sysWechatPay/listRefund`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 分页查询支付列表 🔖
+         * @param {WechatPayPageInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysWechatPayPagePost: async (body?: WechatPayPageInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/sysWechatPay/page`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 微信支付成功回调(商户直连)
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
@@ -121,7 +224,56 @@ export const SysWechatPayApiAxiosParamCreator = function (configuration?: Config
         },
         /**
          * 
-         * @summary 获取支付订单详情 🔖
+         * @summary 获取支付订单详情(微信接口) 🔖
+         * @param {string} tradeId 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysWechatPayPayInfoFromWechatTradeIdGet: async (tradeId: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'tradeId' is not null or undefined
+            if (tradeId === null || tradeId === undefined) {
+                throw new RequiredError('tradeId','Required parameter tradeId was null or undefined when calling apiSysWechatPayPayInfoFromWechatTradeIdGet.');
+            }
+            const localVarPath = `/api/sysWechatPay/payInfoFromWechat/{tradeId}`
+                .replace(`{${"tradeId"}}`, encodeURIComponent(String(tradeId)));
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 获取支付订单详情(本地库) 🔖
          * @param {string} tradeId 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
@@ -213,7 +365,7 @@ export const SysWechatPayApiAxiosParamCreator = function (configuration?: Config
         },
         /**
          * 
-         * @summary 微信支付统一下单获取Id(服务商模式) 🔖
+         * @summary 微信支付下单(服务商模式) 🔖
          * @param {WechatPayTransactionInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
@@ -261,7 +413,55 @@ export const SysWechatPayApiAxiosParamCreator = function (configuration?: Config
         },
         /**
          * 
-         * @summary 微信支付统一下单获取Id(商户直连) 🔖
+         * @summary 微信支付下单(商户直连)Native
+         * @param {WechatPayTransactionInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysWechatPayPayTransactionNativePost: async (body?: WechatPayTransactionInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/sysWechatPay/payTransactionNative`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 微信支付下单(商户直连) 🔖
          * @param {WechatPayTransactionInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
@@ -302,6 +502,103 @@ export const SysWechatPayApiAxiosParamCreator = function (configuration?: Config
             const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
             localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
 
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 退款申请
+         * @param {WechatPayRefundDomesticInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysWechatPayRefundDomesticPost: async (body?: WechatPayRefundDomesticInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/sysWechatPay/refundDomestic`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 获取退款订单详情(微信接口)
+         * @param {string} refundId 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysWechatPayRefundInfoFromWechatRefundIdGet: async (refundId: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'refundId' is not null or undefined
+            if (refundId === null || refundId === undefined) {
+                throw new RequiredError('refundId','Required parameter refundId was null or undefined when calling apiSysWechatPayRefundInfoFromWechatRefundIdGet.');
+            }
+            const localVarPath = `/api/sysWechatPay/refundInfoFromWechat/{refundId}`
+                .replace(`{${"refundId"}}`, encodeURIComponent(String(refundId)));
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
             return {
                 url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
                 options: localVarRequestOptions,
@@ -323,7 +620,7 @@ export const SysWechatPayApiFp = function(configuration?: Configuration) {
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async apiSysWechatPayGenerateParametersForJsapiPayPost(body?: WechatPayParaInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultObject>>> {
+        async apiSysWechatPayGenerateParametersForJsapiPayPost(body?: WechatPayParaInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultWechatPayParaOutput>>> {
             const localVarAxiosArgs = await SysWechatPayApiAxiosParamCreator(configuration).apiSysWechatPayGenerateParametersForJsapiPayPost(body, options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
@@ -332,7 +629,35 @@ export const SysWechatPayApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 微信支付成功回调(商户直连) 🔖
+         * @summary 查询退款信息列表
+         * @param {string} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysWechatPayListRefundPost(body?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultListSysWechatRefund>>> {
+            const localVarAxiosArgs = await SysWechatPayApiAxiosParamCreator(configuration).apiSysWechatPayListRefundPost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 分页查询支付列表 🔖
+         * @param {WechatPayPageInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysWechatPayPagePost(body?: WechatPayPageInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSqlSugarPagedListSysWechatPay>>> {
+            const localVarAxiosArgs = await SysWechatPayApiAxiosParamCreator(configuration).apiSysWechatPayPagePost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 微信支付成功回调(商户直连)
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
@@ -345,7 +670,21 @@ export const SysWechatPayApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 获取支付订单详情 🔖
+         * @summary 获取支付订单详情(微信接口) 🔖
+         * @param {string} tradeId 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysWechatPayPayInfoFromWechatTradeIdGet(tradeId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSysWechatPay>>> {
+            const localVarAxiosArgs = await SysWechatPayApiAxiosParamCreator(configuration).apiSysWechatPayPayInfoFromWechatTradeIdGet(tradeId, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 获取支付订单详情(本地库) 🔖
          * @param {string} tradeId 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
@@ -372,7 +711,7 @@ export const SysWechatPayApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 微信支付统一下单获取Id(服务商模式) 🔖
+         * @summary 微信支付下单(服务商模式) 🔖
          * @param {WechatPayTransactionInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
@@ -386,18 +725,60 @@ export const SysWechatPayApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 微信支付统一下单获取Id(商户直连) 🔖
+         * @summary 微信支付下单(商户直连)Native
          * @param {WechatPayTransactionInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async apiSysWechatPayPayTransactionPost(body?: WechatPayTransactionInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultObject>>> {
+        async apiSysWechatPayPayTransactionNativePost(body?: WechatPayTransactionInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultObject>>> {
+            const localVarAxiosArgs = await SysWechatPayApiAxiosParamCreator(configuration).apiSysWechatPayPayTransactionNativePost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 微信支付下单(商户直连) 🔖
+         * @param {WechatPayTransactionInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysWechatPayPayTransactionPost(body?: WechatPayTransactionInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultWechatPayTransactionOutput>>> {
             const localVarAxiosArgs = await SysWechatPayApiAxiosParamCreator(configuration).apiSysWechatPayPayTransactionPost(body, options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
             };
         },
+        /**
+         * 
+         * @summary 退款申请
+         * @param {WechatPayRefundDomesticInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysWechatPayRefundDomesticPost(body?: WechatPayRefundDomesticInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultObject>>> {
+            const localVarAxiosArgs = await SysWechatPayApiAxiosParamCreator(configuration).apiSysWechatPayRefundDomesticPost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 获取退款订单详情(微信接口)
+         * @param {string} refundId 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysWechatPayRefundInfoFromWechatRefundIdGet(refundId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSysWechatRefund>>> {
+            const localVarAxiosArgs = await SysWechatPayApiAxiosParamCreator(configuration).apiSysWechatPayRefundInfoFromWechatRefundIdGet(refundId, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
     }
 };
 
@@ -414,12 +795,32 @@ export const SysWechatPayApiFactory = function (configuration?: Configuration, b
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async apiSysWechatPayGenerateParametersForJsapiPayPost(body?: WechatPayParaInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultObject>> {
+        async apiSysWechatPayGenerateParametersForJsapiPayPost(body?: WechatPayParaInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultWechatPayParaOutput>> {
             return SysWechatPayApiFp(configuration).apiSysWechatPayGenerateParametersForJsapiPayPost(body, options).then((request) => request(axios, basePath));
         },
         /**
          * 
-         * @summary 微信支付成功回调(商户直连) 🔖
+         * @summary 查询退款信息列表
+         * @param {string} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysWechatPayListRefundPost(body?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultListSysWechatRefund>> {
+            return SysWechatPayApiFp(configuration).apiSysWechatPayListRefundPost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 分页查询支付列表 🔖
+         * @param {WechatPayPageInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysWechatPayPagePost(body?: WechatPayPageInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSqlSugarPagedListSysWechatPay>> {
+            return SysWechatPayApiFp(configuration).apiSysWechatPayPagePost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 微信支付成功回调(商户直连)
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
@@ -428,7 +829,17 @@ export const SysWechatPayApiFactory = function (configuration?: Configuration, b
         },
         /**
          * 
-         * @summary 获取支付订单详情 🔖
+         * @summary 获取支付订单详情(微信接口) 🔖
+         * @param {string} tradeId 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysWechatPayPayInfoFromWechatTradeIdGet(tradeId: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSysWechatPay>> {
+            return SysWechatPayApiFp(configuration).apiSysWechatPayPayInfoFromWechatTradeIdGet(tradeId, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 获取支付订单详情(本地库) 🔖
          * @param {string} tradeId 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
@@ -447,7 +858,7 @@ export const SysWechatPayApiFactory = function (configuration?: Configuration, b
         },
         /**
          * 
-         * @summary 微信支付统一下单获取Id(服务商模式) 🔖
+         * @summary 微信支付下单(服务商模式) 🔖
          * @param {WechatPayTransactionInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
@@ -457,14 +868,44 @@ export const SysWechatPayApiFactory = function (configuration?: Configuration, b
         },
         /**
          * 
-         * @summary 微信支付统一下单获取Id(商户直连) 🔖
+         * @summary 微信支付下单(商户直连)Native
          * @param {WechatPayTransactionInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async apiSysWechatPayPayTransactionPost(body?: WechatPayTransactionInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultObject>> {
+        async apiSysWechatPayPayTransactionNativePost(body?: WechatPayTransactionInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultObject>> {
+            return SysWechatPayApiFp(configuration).apiSysWechatPayPayTransactionNativePost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 微信支付下单(商户直连) 🔖
+         * @param {WechatPayTransactionInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysWechatPayPayTransactionPost(body?: WechatPayTransactionInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultWechatPayTransactionOutput>> {
             return SysWechatPayApiFp(configuration).apiSysWechatPayPayTransactionPost(body, options).then((request) => request(axios, basePath));
         },
+        /**
+         * 
+         * @summary 退款申请
+         * @param {WechatPayRefundDomesticInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysWechatPayRefundDomesticPost(body?: WechatPayRefundDomesticInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultObject>> {
+            return SysWechatPayApiFp(configuration).apiSysWechatPayRefundDomesticPost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 获取退款订单详情(微信接口)
+         * @param {string} refundId 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysWechatPayRefundInfoFromWechatRefundIdGet(refundId: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSysWechatRefund>> {
+            return SysWechatPayApiFp(configuration).apiSysWechatPayRefundInfoFromWechatRefundIdGet(refundId, options).then((request) => request(axios, basePath));
+        },
     };
 };
 
@@ -483,12 +924,34 @@ export class SysWechatPayApi extends BaseAPI {
      * @throws {RequiredError}
      * @memberof SysWechatPayApi
      */
-    public async apiSysWechatPayGenerateParametersForJsapiPayPost(body?: WechatPayParaInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultObject>> {
+    public async apiSysWechatPayGenerateParametersForJsapiPayPost(body?: WechatPayParaInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultWechatPayParaOutput>> {
         return SysWechatPayApiFp(this.configuration).apiSysWechatPayGenerateParametersForJsapiPayPost(body, options).then((request) => request(this.axios, this.basePath));
     }
     /**
      * 
-     * @summary 微信支付成功回调(商户直连) 🔖
+     * @summary 查询退款信息列表
+     * @param {string} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysWechatPayApi
+     */
+    public async apiSysWechatPayListRefundPost(body?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultListSysWechatRefund>> {
+        return SysWechatPayApiFp(this.configuration).apiSysWechatPayListRefundPost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 分页查询支付列表 🔖
+     * @param {WechatPayPageInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysWechatPayApi
+     */
+    public async apiSysWechatPayPagePost(body?: WechatPayPageInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSqlSugarPagedListSysWechatPay>> {
+        return SysWechatPayApiFp(this.configuration).apiSysWechatPayPagePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 微信支付成功回调(商户直连)
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
      * @memberof SysWechatPayApi
@@ -498,7 +961,18 @@ export class SysWechatPayApi extends BaseAPI {
     }
     /**
      * 
-     * @summary 获取支付订单详情 🔖
+     * @summary 获取支付订单详情(微信接口) 🔖
+     * @param {string} tradeId 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysWechatPayApi
+     */
+    public async apiSysWechatPayPayInfoFromWechatTradeIdGet(tradeId: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSysWechatPay>> {
+        return SysWechatPayApiFp(this.configuration).apiSysWechatPayPayInfoFromWechatTradeIdGet(tradeId, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 获取支付订单详情(本地库) 🔖
      * @param {string} tradeId 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
@@ -519,7 +993,7 @@ export class SysWechatPayApi extends BaseAPI {
     }
     /**
      * 
-     * @summary 微信支付统一下单获取Id(服务商模式) 🔖
+     * @summary 微信支付下单(服务商模式) 🔖
      * @param {WechatPayTransactionInput} [body] 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
@@ -530,13 +1004,46 @@ export class SysWechatPayApi extends BaseAPI {
     }
     /**
      * 
-     * @summary 微信支付统一下单获取Id(商户直连) 🔖
+     * @summary 微信支付下单(商户直连)Native
      * @param {WechatPayTransactionInput} [body] 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
      * @memberof SysWechatPayApi
      */
-    public async apiSysWechatPayPayTransactionPost(body?: WechatPayTransactionInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultObject>> {
+    public async apiSysWechatPayPayTransactionNativePost(body?: WechatPayTransactionInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultObject>> {
+        return SysWechatPayApiFp(this.configuration).apiSysWechatPayPayTransactionNativePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 微信支付下单(商户直连) 🔖
+     * @param {WechatPayTransactionInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysWechatPayApi
+     */
+    public async apiSysWechatPayPayTransactionPost(body?: WechatPayTransactionInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultWechatPayTransactionOutput>> {
         return SysWechatPayApiFp(this.configuration).apiSysWechatPayPayTransactionPost(body, options).then((request) => request(this.axios, this.basePath));
     }
+    /**
+     * 
+     * @summary 退款申请
+     * @param {WechatPayRefundDomesticInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysWechatPayApi
+     */
+    public async apiSysWechatPayRefundDomesticPost(body?: WechatPayRefundDomesticInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultObject>> {
+        return SysWechatPayApiFp(this.configuration).apiSysWechatPayRefundDomesticPost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 获取退款订单详情(微信接口)
+     * @param {string} refundId 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysWechatPayApi
+     */
+    public async apiSysWechatPayRefundInfoFromWechatRefundIdGet(refundId: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSysWechatRefund>> {
+        return SysWechatPayApiFp(this.configuration).apiSysWechatPayRefundInfoFromWechatRefundIdGet(refundId, options).then((request) => request(this.axios, this.basePath));
+    }
 }

+ 8 - 8
Web/src/api-services/models/add-org-input.ts

@@ -118,14 +118,6 @@ export interface AddOrgInput {
      */
     level?: number | null;
 
-    /**
-     * 机构类型-数据字典
-     *
-     * @type {string}
-     * @memberof AddOrgInput
-     */
-    type?: string | null;
-
     /**
      * 负责人Id
      *
@@ -179,4 +171,12 @@ export interface AddOrgInput {
      * @memberof AddOrgInput
      */
     name: string;
+
+    /**
+     * 机构类型
+     *
+     * @type {string}
+     * @memberof AddOrgInput
+     */
+    type?: string | null;
 }

+ 71 - 0
Web/src/api-services/models/admin-result-list-sys-wechat-refund.ts

@@ -0,0 +1,71 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { SysWechatRefund } from './sys-wechat-refund';
+ /**
+ * 全局返回结果
+ *
+ * @export
+ * @interface AdminResultListSysWechatRefund
+ */
+export interface AdminResultListSysWechatRefund {
+
+    /**
+     * 状态码
+     *
+     * @type {number}
+     * @memberof AdminResultListSysWechatRefund
+     */
+    code?: number;
+
+    /**
+     * 类型success、warning、error
+     *
+     * @type {string}
+     * @memberof AdminResultListSysWechatRefund
+     */
+    type?: string | null;
+
+    /**
+     * 错误信息
+     *
+     * @type {string}
+     * @memberof AdminResultListSysWechatRefund
+     */
+    message?: string | null;
+
+    /**
+     * 数据
+     *
+     * @type {Array<SysWechatRefund>}
+     * @memberof AdminResultListSysWechatRefund
+     */
+    result?: Array<SysWechatRefund> | null;
+
+    /**
+     * 附加数据
+     *
+     * @type {any}
+     * @memberof AdminResultListSysWechatRefund
+     */
+    extras?: any | null;
+
+    /**
+     * 时间
+     *
+     * @type {Date}
+     * @memberof AdminResultListSysWechatRefund
+     */
+    time?: Date;
+}

+ 69 - 0
Web/src/api-services/models/admin-result-sql-sugar-paged-list-sys-wechat-pay.ts

@@ -0,0 +1,69 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { SqlSugarPagedListSysWechatPay } from './sql-sugar-paged-list-sys-wechat-pay';
+ /**
+ * 全局返回结果
+ *
+ * @export
+ * @interface AdminResultSqlSugarPagedListSysWechatPay
+ */
+export interface AdminResultSqlSugarPagedListSysWechatPay {
+
+    /**
+     * 状态码
+     *
+     * @type {number}
+     * @memberof AdminResultSqlSugarPagedListSysWechatPay
+     */
+    code?: number;
+
+    /**
+     * 类型success、warning、error
+     *
+     * @type {string}
+     * @memberof AdminResultSqlSugarPagedListSysWechatPay
+     */
+    type?: string | null;
+
+    /**
+     * 错误信息
+     *
+     * @type {string}
+     * @memberof AdminResultSqlSugarPagedListSysWechatPay
+     */
+    message?: string | null;
+
+    /**
+     * @type {SqlSugarPagedListSysWechatPay}
+     * @memberof AdminResultSqlSugarPagedListSysWechatPay
+     */
+    result?: SqlSugarPagedListSysWechatPay;
+
+    /**
+     * 附加数据
+     *
+     * @type {any}
+     * @memberof AdminResultSqlSugarPagedListSysWechatPay
+     */
+    extras?: any | null;
+
+    /**
+     * 时间
+     *
+     * @type {Date}
+     * @memberof AdminResultSqlSugarPagedListSysWechatPay
+     */
+    time?: Date;
+}

+ 69 - 0
Web/src/api-services/models/admin-result-sys-wechat-refund.ts

@@ -0,0 +1,69 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { SysWechatRefund } from './sys-wechat-refund';
+ /**
+ * 全局返回结果
+ *
+ * @export
+ * @interface AdminResultSysWechatRefund
+ */
+export interface AdminResultSysWechatRefund {
+
+    /**
+     * 状态码
+     *
+     * @type {number}
+     * @memberof AdminResultSysWechatRefund
+     */
+    code?: number;
+
+    /**
+     * 类型success、warning、error
+     *
+     * @type {string}
+     * @memberof AdminResultSysWechatRefund
+     */
+    type?: string | null;
+
+    /**
+     * 错误信息
+     *
+     * @type {string}
+     * @memberof AdminResultSysWechatRefund
+     */
+    message?: string | null;
+
+    /**
+     * @type {SysWechatRefund}
+     * @memberof AdminResultSysWechatRefund
+     */
+    result?: SysWechatRefund;
+
+    /**
+     * 附加数据
+     *
+     * @type {any}
+     * @memberof AdminResultSysWechatRefund
+     */
+    extras?: any | null;
+
+    /**
+     * 时间
+     *
+     * @type {Date}
+     * @memberof AdminResultSysWechatRefund
+     */
+    time?: Date;
+}

+ 69 - 0
Web/src/api-services/models/admin-result-wechat-pay-para-output.ts

@@ -0,0 +1,69 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { WechatPayParaOutput } from './wechat-pay-para-output';
+ /**
+ * 全局返回结果
+ *
+ * @export
+ * @interface AdminResultWechatPayParaOutput
+ */
+export interface AdminResultWechatPayParaOutput {
+
+    /**
+     * 状态码
+     *
+     * @type {number}
+     * @memberof AdminResultWechatPayParaOutput
+     */
+    code?: number;
+
+    /**
+     * 类型success、warning、error
+     *
+     * @type {string}
+     * @memberof AdminResultWechatPayParaOutput
+     */
+    type?: string | null;
+
+    /**
+     * 错误信息
+     *
+     * @type {string}
+     * @memberof AdminResultWechatPayParaOutput
+     */
+    message?: string | null;
+
+    /**
+     * @type {WechatPayParaOutput}
+     * @memberof AdminResultWechatPayParaOutput
+     */
+    result?: WechatPayParaOutput;
+
+    /**
+     * 附加数据
+     *
+     * @type {any}
+     * @memberof AdminResultWechatPayParaOutput
+     */
+    extras?: any | null;
+
+    /**
+     * 时间
+     *
+     * @type {Date}
+     * @memberof AdminResultWechatPayParaOutput
+     */
+    time?: Date;
+}

+ 69 - 0
Web/src/api-services/models/admin-result-wechat-pay-transaction-output.ts

@@ -0,0 +1,69 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { WechatPayTransactionOutput } from './wechat-pay-transaction-output';
+ /**
+ * 全局返回结果
+ *
+ * @export
+ * @interface AdminResultWechatPayTransactionOutput
+ */
+export interface AdminResultWechatPayTransactionOutput {
+
+    /**
+     * 状态码
+     *
+     * @type {number}
+     * @memberof AdminResultWechatPayTransactionOutput
+     */
+    code?: number;
+
+    /**
+     * 类型success、warning、error
+     *
+     * @type {string}
+     * @memberof AdminResultWechatPayTransactionOutput
+     */
+    type?: string | null;
+
+    /**
+     * 错误信息
+     *
+     * @type {string}
+     * @memberof AdminResultWechatPayTransactionOutput
+     */
+    message?: string | null;
+
+    /**
+     * @type {WechatPayTransactionOutput}
+     * @memberof AdminResultWechatPayTransactionOutput
+     */
+    result?: WechatPayTransactionOutput;
+
+    /**
+     * 附加数据
+     *
+     * @type {any}
+     * @memberof AdminResultWechatPayTransactionOutput
+     */
+    extras?: any | null;
+
+    /**
+     * 时间
+     *
+     * @type {Date}
+     * @memberof AdminResultWechatPayTransactionOutput
+     */
+    time?: Date;
+}

+ 38 - 0
Web/src/api-services/models/batch-config-input.ts

@@ -0,0 +1,38 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+ /**
+ * 批量配置参数输入
+ *
+ * @export
+ * @interface BatchConfigInput
+ */
+export interface BatchConfigInput {
+
+    /**
+     * 编码
+     *
+     * @type {string}
+     * @memberof BatchConfigInput
+     */
+    code?: string | null;
+
+    /**
+     * 属性值
+     *
+     * @type {string}
+     * @memberof BatchConfigInput
+     */
+    value?: string | null;
+}

+ 8 - 0
Web/src/api-services/models/enum-entity.ts

@@ -28,6 +28,14 @@ export interface EnumEntity {
      */
     describe?: string | null;
 
+    /**
+     * 枚举的样式
+     *
+     * @type {string}
+     * @memberof EnumEntity
+     */
+    theme?: string | null;
+
     /**
      * 枚举名称
      *

+ 12 - 0
Web/src/api-services/models/index.ts

@@ -54,6 +54,7 @@ export * from './admin-result-list-sys-pos';
 export * from './admin-result-list-sys-region';
 export * from './admin-result-list-sys-user';
 export * from './admin-result-list-sys-user-ext-org';
+export * from './admin-result-list-sys-wechat-refund';
 export * from './admin-result-list-table-output';
 export * from './admin-result-login-output';
 export * from './admin-result-login-user-output';
@@ -79,6 +80,7 @@ export * from './admin-result-sql-sugar-paged-list-sys-plugin';
 export * from './admin-result-sql-sugar-paged-list-sys-print';
 export * from './admin-result-sql-sugar-paged-list-sys-region';
 export * from './admin-result-sql-sugar-paged-list-sys-role';
+export * from './admin-result-sql-sugar-paged-list-sys-wechat-pay';
 export * from './admin-result-sql-sugar-paged-list-sys-wechat-user';
 export * from './admin-result-sql-sugar-paged-list-tenant-output';
 export * from './admin-result-sql-sugar-paged-list-user-output';
@@ -93,13 +95,17 @@ export * from './admin-result-sys-ldap';
 export * from './admin-result-sys-print';
 export * from './admin-result-sys-user';
 export * from './admin-result-sys-wechat-pay';
+export * from './admin-result-sys-wechat-refund';
 export * from './admin-result-visual-db-table';
 export * from './admin-result-wechat-pay-output';
+export * from './admin-result-wechat-pay-para-output';
+export * from './admin-result-wechat-pay-transaction-output';
 export * from './admin-result-wx-open-id-output';
 export * from './admin-result-wx-phone-output';
 export * from './api-output';
 export * from './assembly';
 export * from './base-proc-input';
+export * from './batch-config-input';
 export * from './calendar';
 export * from './calendar-algorithm-type';
 export * from './calendar-week-rule';
@@ -274,6 +280,7 @@ export * from './sql-sugar-paged-list-sys-plugin';
 export * from './sql-sugar-paged-list-sys-print';
 export * from './sql-sugar-paged-list-sys-region';
 export * from './sql-sugar-paged-list-sys-role';
+export * from './sql-sugar-paged-list-sys-wechat-pay';
 export * from './sql-sugar-paged-list-sys-wechat-user';
 export * from './sql-sugar-paged-list-tenant-output';
 export * from './sql-sugar-paged-list-user-output';
@@ -315,6 +322,7 @@ export * from './sys-role';
 export * from './sys-user';
 export * from './sys-user-ext-org';
 export * from './sys-wechat-pay';
+export * from './sys-wechat-refund';
 export * from './sys-wechat-user';
 export * from './table-output';
 export * from './tenant-id-input';
@@ -357,8 +365,12 @@ export * from './visual-column';
 export * from './visual-db-table';
 export * from './visual-table';
 export * from './wechat-pay-output';
+export * from './wechat-pay-page-input';
 export * from './wechat-pay-para-input';
+export * from './wechat-pay-para-output';
+export * from './wechat-pay-refund-domestic-input';
 export * from './wechat-pay-transaction-input';
+export * from './wechat-pay-transaction-output';
 export * from './wechat-user-input';
 export * from './wechat-user-login';
 export * from './wx-open-id-login-input';

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio