Эх сурвалжийг харах

🔥1、升级所有依赖及优化代码 2、升级.NET 8(后续以v2分支为主进行持续更新) 3、推荐大家切换至此分支

zuohuaijun 1 жил өмнө
parent
commit
08391c7347
100 өөрчлөгдсөн 1664 нэмэгдсэн , 2858 устгасан
  1. 1 1
      Admin.NET/Admin.NET.Application/Admin.NET.Application.csproj
  2. 29 12
      Admin.NET/Admin.NET.Application/Configuration/Alipay.json
  3. 21 9
      Admin.NET/Admin.NET.Application/Configuration/App.json
  4. 3 1
      Admin.NET/Admin.NET.Application/Configuration/Cache.json
  5. 1 1
      Admin.NET/Admin.NET.Application/Configuration/Captcha.json
  6. 3 4
      Admin.NET/Admin.NET.Application/Configuration/CodeGen.json
  7. 21 22
      Admin.NET/Admin.NET.Application/Configuration/Database.json
  8. 1 0
      Admin.NET/Admin.NET.Application/Configuration/Email.json
  9. 2 2
      Admin.NET/Admin.NET.Application/Configuration/Enum.json
  10. 4 0
      Admin.NET/Admin.NET.Application/Configuration/EventBus.json
  11. 7 6
      Admin.NET/Admin.NET.Application/Configuration/Logging.json
  12. 5 2
      Admin.NET/Admin.NET.Application/Configuration/SMS.json
  13. 11 2
      Admin.NET/Admin.NET.Application/Configuration/Swagger.json
  14. 29 27
      Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
  15. 2 2
      Admin.NET/Admin.NET.Core/Const/AlipayConst.cs
  16. 149 0
      Admin.NET/Admin.NET.Core/Entity/SysAlipayAuthInfo.cs
  17. 108 0
      Admin.NET/Admin.NET.Core/Entity/SysAlipayTransaction.cs
  18. 2 2
      Admin.NET/Admin.NET.Core/Enum/AlipayCertTypeEnum.cs
  19. 2 2
      Admin.NET/Admin.NET.Core/Enum/AlipayIdentityTypeEnum.cs
  20. 1 1
      Admin.NET/Admin.NET.Core/EventBus/AppEventSubscriber.cs
  21. 2 2
      Admin.NET/Admin.NET.Core/Extension/RequestExtension.cs
  22. 1 2
      Admin.NET/Admin.NET.Core/GlobalUsings.cs
  23. 72 21
      Admin.NET/Admin.NET.Core/Option/AlipayOptions.cs
  24. 5 5
      Admin.NET/Admin.NET.Core/Service/Alipay/AlipayErrorCodes.cs
  25. 0 276
      Admin.NET/Admin.NET.Core/Service/Alipay/AlipayService.cs
  26. 24 12
      Admin.NET/Admin.NET.Core/Service/Alipay/Dto/AlipayInput.cs
  27. 2 2
      Admin.NET/Admin.NET.Core/Service/Alipay/IAlipayNotify.cs
  28. 271 0
      Admin.NET/Admin.NET.Core/Service/Alipay/SysAlipayService.cs
  29. 1 2
      Admin.NET/Admin.NET.Core/Service/Common/Dto/SysCommonInput.cs
  30. 1 2
      Admin.NET/Admin.NET.Core/Service/Common/Dto/SysCommonOutput.cs
  31. 1 2
      Admin.NET/Admin.NET.Core/Service/Common/SysCommonService.cs
  32. 1 1
      Admin.NET/Admin.NET.Core/Service/Config/SysConfigService.cs
  33. 0 1
      Admin.NET/Admin.NET.Core/Service/Dict/SysDictDataService.cs
  34. 4 2
      Admin.NET/Admin.NET.Core/Service/Dict/SysDictTypeService.cs
  35. 16 11
      Admin.NET/Admin.NET.Core/Service/File/Dto/FileInput.cs
  36. 24 9
      Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs
  37. 8 8
      Admin.NET/Admin.NET.Core/Service/Job/JobClusterServer.cs
  38. 15 8
      Admin.NET/Admin.NET.Core/Service/Job/JobMonitor.cs
  39. 12 1
      Admin.NET/Admin.NET.Core/Service/Job/SysJobService.cs
  40. 3 3
      Admin.NET/Admin.NET.Core/Service/Message/SysSmsService.cs
  41. 6 4
      Admin.NET/Admin.NET.Core/Service/Server/SysServerService.cs
  42. 2 2
      Admin.NET/Admin.NET.Core/Service/Template/Dto/SysTemplateInput.cs
  43. 0 1
      Admin.NET/Admin.NET.Core/Service/Template/Dto/SysTemplateOutput.cs
  44. 2 0
      Admin.NET/Admin.NET.Core/SignatureAuth/SignatureAuthenticationHandler.cs
  45. 1 0
      Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarFilter.cs
  46. 8 6
      Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs
  47. 2 1
      Admin.NET/Admin.NET.Core/Utils/CommonUtil.cs
  48. 114 20
      Admin.NET/Admin.NET.Core/Utils/ComputerUtil.cs
  49. 57 0
      Admin.NET/Admin.NET.Core/Utils/PathTreeBuilder.cs
  50. 2 2
      Admin.NET/Admin.NET.Core/Utils/SafeMath.cs
  51. 2 2
      Admin.NET/Admin.NET.Test/Admin.NET.Test.csproj
  52. 3 3
      Admin.NET/Admin.NET.Test/BaseTest.cs
  53. 0 1
      Admin.NET/Admin.NET.Test/User/UserTest.cs
  54. 2 2
      Admin.NET/Admin.NET.Test/Utils/DateTimeUtilTests.cs
  55. 2 2
      Admin.NET/Admin.NET.Web.Core/Admin.NET.Web.Core.csproj
  56. 1 1
      Admin.NET/Admin.NET.Web.Core/Startup.cs
  57. 1 1
      Admin.NET/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj
  58. 1 1
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Admin.NET.Plugin.ApprovalFlow.csproj
  59. 1 1
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Const/ApprovalFlowConst.cs
  60. 6 6
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Entity/ApprovalFlow.cs
  61. 50 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Entity/ApprovalFlowRecord.cs
  62. 62 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Entity/ApprovalForm.cs
  63. 56 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Entity/ApprovalFormRecord.cs
  64. 0 2
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Enum/FlowTypeEnum.cs
  65. 1 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/GlobalUsings.cs
  66. 7 3
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Middleware/ApprovalFlowMiddleware.cs
  67. 1 2
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/SeedData/SysMenuSeedData.cs
  68. 0 27
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/SeedData/SysTenantMenuSeedData.cs
  69. 38 29
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/ApprovalFlowService.cs
  70. 95 11
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/Dto/ApprovalFlowInput.cs
  71. 96 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/Dto/ApprovalFlowItem.cs
  72. 27 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/Dto/ApprovalFormItem.cs
  73. 81 0
      Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/SysApproval/SysApprovalService.cs
  74. 1 4
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Admin.NET.Plugin.DingTalk.csproj
  75. 3 5
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Configuration/DingTalk.json
  76. 3 13
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Const/DingTalkConst.cs
  77. 2 2
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkUser.cs
  78. 1 2
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/GlobalUsings.cs
  79. 61 61
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Job/SyncDingTalkUserJob.cs
  80. 1 11
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Option/DingTalkOptions.cs
  81. 0 58
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/AliTripRequest.cs
  82. 0 70
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/Enums.cs
  83. 0 85
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/GetAlitripAddressRequest.cs
  84. 0 25
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/GetAlitripAddressResponse.cs
  85. 0 104
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/GetAlitripFlightOrdersRequest.cs
  86. 0 511
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/GetAlitripFlightOrdersResponse.cs
  87. 0 104
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/GetAlitripTrainOrdersRequest.cs
  88. 0 299
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/GetAlitripTrainOrdersResponse.cs
  89. 0 21
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/IAliTripRequestProxy.cs
  90. 0 166
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/AttendanceRequest.cs
  91. 0 109
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/AttdendanceApprovalsFinishRequest.cs
  92. 0 55
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/AttendanceApprovalsFinishResponse.cs
  93. 0 29
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/AttendanceApprovelsCancelRequest.cs
  94. 0 16
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/AttendanceApprovelsCancelResponse.cs
  95. 0 39
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/Enums.cs
  96. 0 38
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/GetLeaveStatusRequest.cs
  97. 0 73
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/GetLeaveStatusResponse.cs
  98. 0 43
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/ListRecordRequest.cs
  99. 0 294
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/ListRecordResponse.cs
  100. 0 25
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/IAttendanceRequestProxy.cs

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

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
     <NoWarn>1701;1702;1591;8632</NoWarn>
     <DocumentationFile></DocumentationFile>
     <ImplicitUsings>enable</ImplicitUsings>

+ 29 - 12
Admin.NET/Admin.NET.Application/Configuration/Alipay.json

@@ -1,17 +1,34 @@
 {
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
-  "Alipay": { // 支付宝支付配置,文档地址:https://openhome.alipay.com/develop/sandbox/app
-    "AppId": "9000000000000000", // 支付宝 APPID
-    "AlipayWebsocketUrl": "openchannel-sandbox.dl.alipaydev.com", // websocket服务地址
-    "ServerUrl": "https://openapi-sandbox.dl.alipaydev.com/gateway.do",  // 支付宝网关地址
-    "AuthUrl": "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm", // 授权回调地址
-    "NotifyUrl": "http://xxxx.xxx/api/Alipay/Notify", // 应用网关地址
-    "PrivateKey": "xxxxxxxxx", // 应用私钥
-    "SignType": "RSA2", // 加密算法
-    "EncryptKey": "xxxxxxxx", // 从支付宝获取敏感信息时的加密密钥
-    "AlipayPublicCertPath": "/AlipayCrt/alipayPublicCert.crt", // 支付宝公钥证书存放路径
-    "RootCertPath": "/AlipayCrt/alipayRootCert.crt", // 支付宝根证书存放路径
-    "AppCertPath": "/AlipayCrt/appPublicCert.crt" // 应用公钥证书存放路径
+  // 支付宝支付配置,文档地址:https://openhome.alipay.com/develop/sandbox/app
+  "Alipay": {
+    "ServerUrl": "https://openapi-sandbox.dl.alipaydev.com/gateway.do", // 支付宝网关地址
+    "WebsocketUrl": "openchannel-sandbox.dl.alipaydev.com", // websocket服务地址
+    //"AuthUrl": "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm", // 正式环境授权回调地址
+    "AuthUrl": "https://openauth-sandbox.dl.alipaydev.com/oauth2/publicAppAuthorize.htm", // 沙箱环境授权回调地址
+    "AppAuthUrl": "http://xxxxxxxxxx", // 应用授权回调地址
+    "NotifyUrl": "http://xxxxxxxxx/api/sysAlipay/Notify", // 应用网关地址
+    "RootCertPath": "Alipaycrt/alipayRootCert.crt", // 支付宝根证书存放路径
+    "AccountList": [
+      {
+        "Name": "sandbox 默认应用",
+        "AppId": "xxxxxxxxxxxxxx",
+        "SignType": "RSA2",
+        "PrivateKey": "xxxxxxxxxxxxxxxxx",
+        "EncryptKey": "xxxxxxxxxxxxxxxxxxxx",
+        "AppCertPath": "Alipaycrt/appPublicCert.crt", // 应用公钥证书存放路径
+        "AlipayPublicCertPath": "Alipaycrt/alipayPublicCert.crt" // 支付宝公钥证书存放路径
+      },
+      {
+        "Name": "sandbox 默认应用2",
+        "AppId": "xxxxxxxxxxxxxx",
+        "SignType": "RSA2",
+        "PrivateKey": "xxxxxxxxxxxxxxxxx",
+        "EncryptKey": "xxxxxxxxxxxxxxxxxxxx",
+        "AppCertPath": "Alipaycrt/appPublicCert.crt", // 应用公钥证书存放路径
+        "AlipayPublicCertPath": "Alipaycrt/alipayPublicCert.crt" // 支付宝公钥证书存放路径
+      }
+    ]
   }
 }

+ 21 - 9
Admin.NET/Admin.NET.Application/Configuration/App.json

@@ -1,14 +1,13 @@
 {
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
-  "Urls": "http://*:5005", // 配置默认端口
-  // "https_port": 44325,
-
-  "AllowedHosts": "*",
+  "Urls": "http://*:5005", // 默认端口
+  "AllowedHosts": "*", // 允许所有地址
 
   "AppSettings": {
     "InjectSpecificationDocument": true, // 生产环境是否开启Swagger
-    "ExternalAssemblies": [ "plugins" ] // 插件目录
+    "ExternalAssemblies": [ "plugins" ], // 插件目录
+    "VirtualPath": "" // 二级虚拟目录
   },
   "DynamicApiControllerSettings": {
     //"DefaultRoutePrefix": "api", // 默认路由前缀
@@ -24,23 +23,36 @@
     "ThrowBah": true, // 是否将 Oops.Oh 默认抛出为业务异常
     "LogError": false // 是否输出异常日志
   },
+  // 静态资源处理方式(允许这些文件被访问),包含".*": "application/octet-stream"允许访问所有静态资源
+  "StaticContentTypeMappings": {
+    ".dll": "application/octet-stream",
+    ".exe": "application/octet-stream",
+    ".pdb": "application/octet-stream",
+    ".so": "application/octet-stream"
+  },
   "LocalizationSettings": {
     "SupportedCultures": [ "zh-CN", "en" ], // 语言列表
     "DefaultCulture": "zh-CN", // 默认语言
     "DateTimeFormatCulture": "zh-CN" // 固定时间区域为特定时区(多语言)
   },
   "CorsAccessorSettings": {
-    //"PolicyName": "App.Cors.Policy",
-    //"WithOrigins": [ "http://localhost:5005", "https://gitee.com" ],
-    "WithExposedHeaders": [ "Content-Disposition", "X-Pagination", "access-token", "x-access-token" ], // 如果前端不代理且是axios请求
+    //"PolicyName": "App.Cors.Policy", // 跨域策略名称
+    //"WithOrigins": [ "http://localhost:5005", "https://gitee.com" ], // 允许的跨域地址
+    "WithExposedHeaders": [ "Content-Disposition", "X-Pagination", "access-token", "x-access-token", "Access-Control-Expose-Headersx-access-token" ], // 如果前端不代理且是axios请求
     "SignalRSupport": true // 启用 SignalR 跨域支持
   },
+  // 定时任务/作业调度
+  "JobSchedule": {
+    "Enabled": true // 是否开启
+  },
+  // 雪花Id
   "SnowId": {
-    "WorkerId": 1, // 机器码 全局唯一
+    "WorkerId": 1, // 雪花Id机器码,多服务器时全局唯一
     "WorkerIdBitLength": 6, // 机器码位长 默认值6,取值范围 [1, 19]
     "SeqBitLength": 6, // 序列数位长 默认值6,取值范围 [3, 21](建议不小于4,值越大性能越高、Id位数也更长)
     "WorkerPrefix": "adminnet_" // 缓存前缀
   },
+  // 密码策略
   "Cryptogram": {
     "StrongPassword": false, // 是否开启密码强度验证
     "PasswordStrengthValidation": "(?=^.{6,16}$)(?=.*\\d)(?=.*\\W+)(?=.*[A-Z])(?=.*[a-z])(?!.*\\n).*$", // 密码强度验证正则表达式,必须须包含大小写字母、数字和特殊字符的组合,长度在6-16之间

+ 3 - 1
Admin.NET/Admin.NET.Application/Configuration/Cache.json

@@ -1,6 +1,7 @@
 {
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
+  // 缓存配置
   "Cache": {
     "Prefix": "adminnet_", // 全局缓存前缀
     "CacheType": "Memory", // Memory、Redis
@@ -11,7 +12,8 @@
       "AutoDetect": false // 自动检测集群节点 阿里云的Redis分布式集群使用代理模式,需要设置为false关闭自动检测;如果不用代理地址,就配置多个节点地址并打开自动检测
     }
   },
-  "Cluster": { // 集群配置
+  // 集群配置
+  "Cluster": {
     "Enabled": false, // 启用集群:前提开启Redis缓存模式
     "ServerId": "adminnet", // 服务器标识
     "ServerIp": "", // 服务器IP

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

@@ -1,4 +1,4 @@
-{
+{
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
   // Lazy.Captcha.Core 组件详细文档(https://api.gitee.com/pojianbing/lazy-captcha/)

+ 3 - 4
Admin.NET/Admin.NET.Application/Configuration/CodeGen.json

@@ -3,8 +3,8 @@
 
   // 代码生成配置项-程序集名称集合
   "CodeGen": {
-    "EntityAssemblyNames": [ "Admin.NET.Core", "Admin.NET.Application" ],
-    "BaseEntityNames": [ "EntityTenantId", "EntityTenant", "EntityTenantBaseData", "EntityBaseData", "EntityBase", "EntityBaseId" ],
+    "EntityAssemblyNames": [ "Admin.NET.Core", "Admin.NET.Application" ], // 实体所在程序集(自行添加新建程序集名称)
+    "BaseEntityNames": [ "EntityTenantId", "EntityTenant", "EntityTenantBaseData", "EntityBaseData", "EntityBase", "EntityBaseId" ], // 实体基类名称
     "EntityBaseColumn": {
       "EntityTenantId": [ "Id", "TenantId" ],
       "EntityTenant": [ "Id", "CreateTime", "UpdateTime", "CreateUserId", "UpdateUserId", "CreateUserName", "UpdateUserName", "IsDelete", "TenantId" ],
@@ -12,9 +12,8 @@
       "EntityBaseData": [ "Id", "CreateTime", "UpdateTime", "CreateUserId", "UpdateUserId", "CreateUserName", "UpdateUserName", "IsDelete", "CreateOrgId", "CreateOrgName" ],
       "EntityBase": [ "Id", "CreateTime", "UpdateTime", "CreateUserId", "UpdateUserId", "CreateUserName", "UpdateUserName", "IsDelete" ],
       "EntityBaseId": [ "Id" ]
-      //"BaseId": [ "Id" ]
     },
     "FrontRootPath": "Web", // 前端项目根目录
-    "BackendApplicationNamespaces": [ "Admin.NET.Application", "Admin.NET.Application2" ] // 后端生成到的项目
+    "BackendApplicationNamespaces": [ "Admin.NET.Application", "Admin.NET.Application2" ] // 后端生成到的项目(自行添加新建命名空间)
   }
 }

+ 21 - 22
Admin.NET/Admin.NET.Application/Configuration/Database.json

@@ -4,17 +4,16 @@
   // 详细数据库配置见SqlSugar官网(第一个为默认库),极力推荐 PostgreSQL 数据库
   // 数据库连接字符串参考地址:https://www.connectionstrings.com/
   "DbConnection": {
-    "SuperAdminIgnoreIDeletedFilter": false, // 超级管理员是否忽略逻辑删除过滤器
     "EnableConsoleSql": false, // 启用控制台打印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", // 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;Encrypt=True;TrustServerCertificate=True;", // SqlServer 库连接字符串
+        "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;AllowLoadLocalInfile=true;AllowUserVariables=true;", // 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;Encrypt=True;TrustServerCertificate=True;", // SqlServer
 
         //"SlaveConnectionConfigs": [ // 读写分离/主从
         //	{
@@ -27,18 +26,18 @@
         //	}
         //],
         "DbSettings": {
-          "EnableInitDb": true, // 启用库初始化
+          "EnableInitDb": true, // 启用库初始化(若实体没有变化建议关闭)
           "EnableDiffLog": false, // 启用库表差异日志
           "EnableUnderLine": false, // 启用驼峰转下划线
-          "EnableConnStringEncrypt": false // 启用数据库连接串加密
+          "EnableConnEncrypt": false // 启用数据库连接串加密(国密SM2加解密)
         },
         "TableSettings": {
-          "EnableInitTable": true, // 启用表初始化
-          "EnableIncreTable": false // 启用表增量更新-特性[IncreTable]
+          "EnableInitTable": true, // 启用表初始化(若实体没有变化建议关闭)
+          "EnableIncreTable": false // 启用表增量更新(只更新贴了特性[IncreTable]的实体表)
         },
         "SeedSettings": {
-          "EnableInitSeed": true, // 启用种子初始化
-          "EnableIncreSeed": false // 启用种子增量更新-特性[IncreSeed]
+          "EnableInitSeed": true, // 启用种子初始化(若种子没有变化建议关闭)
+          "EnableIncreSeed": false // 启用种子增量更新(只更新贴了特性[IncreSeed]的种子表)
         }
       }
       //// 日志独立数据库配置
@@ -47,17 +46,17 @@
       //  "DbType": "Sqlite",
       //  "ConnectionString": "DataSource=./Admin.NET.Log.db", // 库连接字符串
       //  "DbSettings": {
-      //    "EnableInitDb": true, // 启用库初始化
+      //    "EnableInitDb": true, // 启用库初始化(若实体没有变化建议关闭)
       //    "EnableDiffLog": false, // 启用库表差异日志
       //    "EnableUnderLine": false // 启用驼峰转下划线
       //  },
       //  "TableSettings": {
-      //    "EnableInitTable": true, // 启用表初始化
-      //    "EnableIncreTable": false // 启用表增量更新-特性[IncreTable]
+      //    "EnableInitTable": true, // 启用表初始化(若实体没有变化建议关闭)
+      //    "EnableIncreTable": false // 启用表增量更新(只更新贴了特性[IncreTable]的实体表)
       //  },
       //  "SeedSettings": {
-      //    "EnableInitSeed": false, // 启用种子初始化
-      //    "EnableIncreSeed": false // 启用种子增量更新-特性[IncreSeed]
+      //    "EnableInitSeed": false, // 启用种子初始化(若种子没有变化建议关闭)
+      //    "EnableIncreSeed": false // 启用种子增量更新(只更新贴了特性[IncreSeed]的种子表)
       //  }
       //},
       //// 其他数据库配置(可以配置多个)
@@ -66,17 +65,17 @@
       //  "DbType": "Sqlite", // 库类型
       //  "ConnectionString": "DataSource=./Admin.NET.Test.db", // 库连接字符串
       //  "DbSettings": {
-      //    "EnableInitDb": true, // 启用库初始化
+      //    "EnableInitDb": true, // 启用库初始化(若实体没有变化建议关闭)
       //    "EnableDiffLog": false, // 启用库表差异日志
       //    "EnableUnderLine": false // 启用驼峰转下划线
       //  },
       //  "TableSettings": {
-      //    "EnableInitTable": true, // 启用表初始化
-      //    "EnableIncreTable": false // 启用表增量更新-特性[IncreTable]
+      //    "EnableInitTable": true, // 启用表初始化(若实体没有变化建议关闭)
+      //    "EnableIncreTable": false // 启用表增量更新(只更新贴了特性[IncreTable]的实体表)
       //  },
       //  "SeedSettings": {
-      //    "EnableInitSeed": true, // 启用种子初始化
-      //    "EnableIncreSeed": false // 启用种子增量更新-特性[IncreSeed]
+      //    "EnableInitSeed": true, // 启用种子初始化(若种子没有变化建议关闭)
+      //    "EnableIncreSeed": false // 启用种子增量更新(只更新贴了特性[IncreSeed]的种子表)
       //  }
       //}
     ]

+ 1 - 0
Admin.NET/Admin.NET.Application/Configuration/Email.json

@@ -1,6 +1,7 @@
 {
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
+  // 邮箱配置
   "Email": {
     "Host": "smtp.163.com", // 主机
     "Port": 465, // 端口 465、994、25

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

@@ -1,8 +1,8 @@
 {
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
-  // 枚举实体所在程序集名称集合
+  // 枚举配置(将所有枚举转成字典)
   "Enum": {
-    "EntityAssemblyNames": [ "Admin." ]
+    "EntityAssemblyNames": [ "Admin." ] // 枚举所在程序集(自行添加新建程序集名称)
   }
 }

+ 4 - 0
Admin.NET/Admin.NET.Application/Configuration/EventBus.json

@@ -2,11 +2,15 @@
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
   "EventBus": {
+    // 事件源存储器类型,默认内存存储(Redis则需要配合缓存相关配置)
+    "EventSourceType": "Memory", // Memory、Redis、RabbitMQ、Kafka
     "RabbitMQ": {
       "UserName": "adminnet",
       "Password": "adminnet++123456",
       "HostName": "127.0.0.1",
       "Port": 5672
+    },
+    "Kafka": {
     }
   }
 }

+ 7 - 6
Admin.NET/Admin.NET.Application/Configuration/Logging.json

@@ -6,19 +6,20 @@
       "Default": "Information",
       "Microsoft.AspNetCore": "Warning",
       "Microsoft.EntityFrameworkCore": "Information",
-      "AspNetCoreRateLimit": "None"
+      "AspNetCoreRateLimit": "None",
+      "System.Net.Http.HttpClient": "Warning"
     },
     "File": {
-      "Enabled": false, // 启用文件日志
+      "Enabled": true, // 启用文件日志
       "FileName": "logs/{0:yyyyMMdd}_{1}.log", // 日志文件
       "Append": true, // 追加覆盖
-      // "MinimumLevel": "Information", // 日志级别
+      "MinimumLevel": "Error", // 日志级别
       "FileSizeLimitBytes": 10485760, // 10M=10*1024*1024
       "MaxRollingFiles": 30 // 只保留30个文件
     },
     "Database": {
       "Enabled": true, // 启用数据库日志
-      "MinimumLevel": "Information"
+      "MinimumLevel": "Information" // 日志级别
     },
     "ElasticSearch": {
       "Enabled": false, // 启用ES日志
@@ -33,12 +34,12 @@
       "DefaultIndex": "adminnet" // 索引
     },
     "Monitor": {
-      "GlobalEnabled": true, // 启用全局拦截日志
+      "GlobalEnabled": true, // 启用全局拦截日志(建议生产环境关闭,否则对性能有影响)
       "IncludeOfMethods": [], // 拦截特定方法,当GlobalEnabled=false有效
       "ExcludeOfMethods": [], // 排除特定方法,当GlobalEnabled=true有效
       "BahLogLevel": "Information", // Oops.Oh 和 Oops.Bah 业务日志输出级别
       "WithReturnValue": true, // 是否包含返回值,默认true
-      "ReturnValueThreshold": 500, // 返回值字符串阈值,默认0全量输出
+      "ReturnValueThreshold": 0, // 返回值字符串阈值,默认0全量输出
       "JsonBehavior": "None", // 是否输出Json,默认None(OnlyJson、All)
       "JsonIndented": false, // 是否格式化Json
       "UseUtcTimestamp": false, // 时间格式UTC、LOCAL

+ 5 - 2
Admin.NET/Admin.NET.Application/Configuration/SMS.json

@@ -1,7 +1,9 @@
 {
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
+  // 短信配置
   "SMS": {
+    // 阿里云短信
     "Aliyun": {
       "AccessKeyId": "",
       "AccessKeySecret": "",
@@ -9,17 +11,18 @@
         {
           "Id": "0",
           "SignName": "AdminNET 平台",
-          "TemplateCode": "SMS_291005708",
+          "TemplateCode": "SMS_xxx",
           "Content": "您的验证码为:${code},请勿泄露于他人!"
         },
         {
           "Id": "1",
           "SignName": "AdminNET 平台",
-          "TemplateCode": "SMS_462801755",
+          "TemplateCode": "SMS_xxx",
           "Content": "注册成功,感谢您的注册,请妥善保管您的账户信息"
         }
       ]
     },
+    // 腾讯云短信
     "Tencentyun": {
       "SdkAppId": "",
       "AccessKeyId": "",

+ 11 - 2
Admin.NET/Admin.NET.Application/Configuration/Swagger.json

@@ -1,4 +1,4 @@
-{
+{
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
 
   "SpecificationDocumentSettings": {
@@ -22,10 +22,19 @@
     "DefaultGroupName": "Default", // 默认分组名
     "DocExpansionState": "List", // List、Full、None
     "EnableAllGroups": true,
+    //"ServerDir": "xxx", // 二级虚拟目录并且开启 Servers 选择列表
+    "HideServers": true,
+    "Servers": [
+      {
+        "Url": "http://ip/xxx",
+        "Description": "二级目录应用程序名"
+      }
+    ],
     "LoginInfo": {
       "Enabled": true, // 是否开启Swagger登录
       "CheckUrl": "/api/swagger/checkUrl",
-      "SubmitUrl": "/api/swagger/submitUrl"
+      "SubmitUrl": "/api/swagger/submitUrl",
+      "EnableOnProduction": false // 是否在生产环境中自动开启
     },
     "EnumToNumber": true // 枚举类型生成值类型
   }

+ 29 - 27
Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
     <NoWarn>1701;1702;1591;8632</NoWarn>
     <DocumentationFile></DocumentationFile>
     <ImplicitUsings>enable</ImplicitUsings>
@@ -13,15 +13,16 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="3.1.0" />
-    <PackageReference Include="AlipaySDKNet.Standard" Version="4.9.370" />
-    <PackageReference Include="AngleSharp" Version="1.1.2" />
+    <PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="3.1.1" />
+    <PackageReference Include="AlipaySDKNet.Standard" Version="4.9.376" />
+    <PackageReference Include="AngleSharp" Version="1.2.0" />
     <PackageReference Include="AspectCore.Extensions.Reflection" Version="2.4.0" />
     <PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
-    <PackageReference Include="Elastic.Clients.Elasticsearch" Version="8.16.3" />
-    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.5.26" />
-    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.5.26" />
-    <PackageReference Include="Furion.Pure" Version="4.9.5.26" />
+    <PackageReference Include="Elastic.Clients.Elasticsearch" Version="8.17.1" />
+    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.3" />
+    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.3" />
+    <PackageReference Include="Furion.Pure" Version="4.9.7.3" />
+	<PackageReference Include="Hardware.Info" Version="101.0.0" />
     <PackageReference Include="Hashids.net" Version="1.7.0" />
     <PackageReference Include="IPTools.China" Version="1.6.0" />
     <PackageReference Include="IPTools.International" Version="1.6.0" />
@@ -29,36 +30,27 @@
     <PackageReference Include="Magicodes.IE.Excel" Version="2.7.5.2" />
     <PackageReference Include="Magicodes.IE.Pdf" Version="2.7.5.2" />
     <PackageReference Include="Magicodes.IE.Word" Version="2.7.5.2" />
-    <PackageReference Include="MailKit" Version="4.9.0" />
-    <PackageReference Include="MiniExcel" Version="1.36.0" />
+    <PackageReference Include="MailKit" Version="4.10.0" />
+    <PackageReference Include="MiniExcel" Version="1.37.0" />
     <PackageReference Include="MiniWord" Version="0.9.2" />
-    <PackageReference Include="NewLife.Redis" Version="6.0.2024.1205" />
+    <PackageReference Include="NewLife.Redis" Version="6.1.2025.202" />
     <PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0" />
+	<PackageReference Include="OnceMi.AspNetCore.OSS" Version="1.2.0" />
     <PackageReference Include="QRCoder" Version="1.6.0" />
     <PackageReference Include="RabbitMQ.Client" Version="7.0.0" />
     <PackageReference Include="SixLabors.ImageSharp.Web" Version="3.1.3" />
-    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.6.0" />
-    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.9.0" />
-    <PackageReference Include="SqlSugarCore" Version="5.1.4.171" />
+    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.7.0" />
+    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.10.0" />
+    <PackageReference Include="SqlSugarCore" Version="5.1.4.175" />
     <PackageReference Include="SSH.NET" Version="2024.2.0" />
-    <PackageReference Include="System.Linq.Dynamic.Core" Version="1.5.1" />
+    <PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.0.1" />
     <PackageReference Include="System.Net.Http" Version="4.3.4" />
     <PackageReference Include="System.Private.Uri" Version="4.3.2" />
-    <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1146" />
+    <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1172" />
     <PackageReference Include="UAParser" Version="3.1.47" />
     <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
     <PackageReference Include="BouncyCastle.Cryptography" Version="2.5.0" Aliases="BouncyCastleV2" />
   </ItemGroup>
-	
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net6.0' ">
-    <PackageReference Include="AspNet.Security.OAuth.Gitee" Version="6.0.15" />
-    <PackageReference Include="AspNet.Security.OAuth.Weixin" Version="6.0.15" />
-    <PackageReference Include="Lazy.Captcha.Core" Version="2.0.6" />
-    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="6.0.36" />
-    <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="6.0.36" />
-    <PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="6.0.36" />
-    <PackageReference Include="OnceMi.AspNetCore.OSS" Version="1.1.9" />
-  </ItemGroup>
 
   <ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
     <PackageReference Include="AspNet.Security.OAuth.Gitee" Version="8.3.0" />
@@ -67,7 +59,17 @@
     <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="8.0.11" />
     <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="8.0.11" />
     <PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="8.0.11" />
-    <PackageReference Include="OnceMi.AspNetCore.OSS" Version="1.2.0" />
+	<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.4.7" />  
+  </ItemGroup>
+
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
+    <PackageReference Include="AspNet.Security.OAuth.Gitee" Version="9.0.0" />
+    <PackageReference Include="AspNet.Security.OAuth.Weixin" Version="9.0.0" />
+    <PackageReference Include="Lazy.Captcha.Core" Version="2.1.0" />
+    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.1" />
+    <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="9.0.1" />
+    <PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="9.0.1" />
+	<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.5.0" />
   </ItemGroup>
 
 </Project>

+ 2 - 2
Admin.NET/Admin.NET.Core/Const/AlipayConst.cs

@@ -1,7 +1,7 @@
 // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-// 
+//
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-// 
+//
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 namespace Admin.NET.Core;

+ 149 - 0
Admin.NET/Admin.NET.Core/Entity/SysAlipayAuthInfo.cs

@@ -0,0 +1,149 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 支付宝授权记录表
+/// </summary>
+[SugarTable(null, "支付宝授权记录表")]
+[SysTable]
+[SugarIndex("index_{table}_U", nameof(UserId), OrderByType.Asc)]
+[SugarIndex("index_{table}_T", nameof(OpenId), OrderByType.Asc)]
+public class SysAlipayAuthInfo : EntityBase
+{
+    /// <summary>
+    /// 商户AppId
+    /// </summary>
+    [SugarColumn(ColumnDescription = "商户AppId", Length = 64)]
+    public string? AppId { get; set; }
+
+    /// <summary>
+    /// 开放ID
+    /// </summary>
+    [SugarColumn(ColumnDescription = "开放ID", Length = 64)]
+    public string? OpenId { get; set; }
+
+    /// <summary>
+    /// 用户ID
+    /// </summary>
+    [SugarColumn(ColumnDescription = "用户ID", Length = 64)]
+    public string? UserId { get; set; }
+
+    /// <summary>
+    /// 性别
+    /// </summary>
+    [SugarColumn(ColumnDescription = "性别", Length = 8)]
+    public GenderEnum Gender { get; set; }
+
+    /// <summary>
+    /// 年龄
+    /// </summary>
+    [SugarColumn(ColumnDescription = "年龄", Length = 16)]
+    public int Age { get; set; }
+
+    /// <summary>
+    /// 手机号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "手机号", Length = 32)]
+    public string Mobile { get; set; }
+
+    /// <summary>
+    /// 显示名称
+    /// </summary>
+    [SugarColumn(ColumnDescription = "显示名称", Length = 128)]
+    public string DisplayName { get; set; }
+
+    /// <summary>
+    /// 昵称
+    /// </summary>
+    [SugarColumn(ColumnDescription = "昵称", Length = 64)]
+    public string NickName { get; set; }
+
+    /// <summary>
+    /// 用户名
+    /// </summary>
+    [SugarColumn(ColumnDescription = "用户名", Length = 64)]
+    public string UserName { get; set; }
+
+    /// <summary>
+    /// 头像
+    /// </summary>
+    [SugarColumn(ColumnDescription = "头像", Length = 512)]
+    public string? Avatar { get; set; }
+
+    /// <summary>
+    /// 邮箱
+    /// </summary>
+    [SugarColumn(ColumnDescription = "邮箱", Length = 128)]
+    public string? Email { get; set; }
+
+    /// <summary>
+    /// 用户民族
+    /// </summary>
+    [SugarColumn(ColumnDescription = "用户民族", Length = 32)]
+    public string? UserNation { get; set; }
+
+    /// <summary>
+    /// 淘宝ID
+    /// </summary>
+    [SugarColumn(ColumnDescription = "淘宝ID", Length = 64)]
+    public string? TaobaoId { get; set; }
+
+    /// <summary>
+    /// 电话
+    /// </summary>
+    [SugarColumn(ColumnDescription = "电话", Length = 32)]
+    public string? Phone { get; set; }
+
+    /// <summary>
+    /// 生日
+    /// </summary>
+    [SugarColumn(ColumnDescription = "生日", Length = 32)]
+    public string? PersonBirthday { get; set; }
+
+    /// <summary>
+    /// 职业
+    /// </summary>
+    [SugarColumn(ColumnDescription = "职业", Length = 64)]
+    public string? Profession { get; set; }
+
+    /// <summary>
+    /// 省份
+    /// </summary>
+    [SugarColumn(ColumnDescription = "省份", Length = 64)]
+    public string? Province { get; set; }
+
+    /// <summary>
+    /// 用户状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "用户状态", Length = 32)]
+    public string? UserStatus { get; set; }
+
+    /// <summary>
+    /// 学历
+    /// </summary>
+    [SugarColumn(ColumnDescription = "学历", Length = 32)]
+    public string? Degree { get; set; }
+
+    /// <summary>
+    /// 用户类型
+    /// </summary>
+    [SugarColumn(ColumnDescription = "用户类型", Length = 32)]
+    public string? UserType { get; set; }
+
+    /// <summary>
+    /// 邮编
+    /// </summary>
+    [SugarColumn(ColumnDescription = "邮编", Length = 16)]
+    public string? Zip { get; set; }
+
+    /// <summary>
+    /// 地址
+    /// </summary>
+    [SugarColumn(ColumnDescription = "地址", Length = 256)]
+    public string? Address { get; set; }
+}

+ 108 - 0
Admin.NET/Admin.NET.Core/Entity/SysAlipayTransaction.cs

@@ -0,0 +1,108 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 支付宝交易记录表
+/// </summary>
+[SugarTable(null, "支付宝交易记录表")]
+[SysTable]
+[SugarIndex("index_{table}_U", nameof(UserId), OrderByType.Asc)]
+[SugarIndex("index_{table}_T", nameof(TradeNo), OrderByType.Asc)]
+[SugarIndex("index_{table}_O", nameof(OutTradeNo), OrderByType.Asc)]
+public class SysAlipayTransaction : EntityBase
+{
+    /// <summary>
+    /// 用户Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "用户Id", Length = 64)]
+    public long UserId { get; set; }
+
+    /// <summary>
+    /// 交易号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易号", Length = 64)]
+    public string? TradeNo { get; set; }
+
+    /// <summary>
+    /// 商户订单号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "商户订单号", Length = 64)]
+    public string OutTradeNo { get; set; }
+
+    /// <summary>
+    /// 交易金额
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易金额", Length = 20)]
+    public decimal TotalAmount { get; set; }
+
+    /// <summary>
+    /// 交易状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易状态", Length = 32)]
+    public string TradeStatus { get; set; }
+
+    /// <summary>
+    /// 交易完成时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易完成时间")]
+    public DateTime? FinishTime { get; set; }
+
+    /// <summary>
+    /// 交易标题
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易标题", Length = 256)]
+    public string Subject { get; set; }
+
+    /// <summary>
+    /// 交易描述
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易描述", Length = 512)]
+    public string? Body { get; set; }
+
+    /// <summary>
+    /// 买家支付宝账号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "买家支付宝账号", Length = 128)]
+    public string? BuyerLogonId { get; set; }
+
+    /// <summary>
+    /// 买家支付宝用户ID
+    /// </summary>
+    [SugarColumn(ColumnDescription = "买家支付宝用户ID", Length = 32)]
+    public string? BuyerUserId { get; set; }
+
+    /// <summary>
+    /// 卖家支付宝用户ID
+    /// </summary>
+    [SugarColumn(ColumnDescription = "卖家支付宝用户ID", Length = 32)]
+    public string? SellerUserId { get; set; }
+
+    /// <summary>
+    /// 商户AppId
+    /// </summary>
+    [SugarColumn(ColumnDescription = "商户AppId", Length = 64)]
+    public string? AppId { get; set; }
+
+    /// <summary>
+    /// 交易扩展信息
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易扩展信息", Length = 1024)]
+    public string? ExtendInfo { get; set; }
+
+    /// <summary>
+    /// 交易异常信息
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易扩展信息", Length = 1024)]
+    public string? ErrorInfo { get; set; }
+
+    /// <summary>
+    /// 交易备注
+    /// </summary>
+    [SugarColumn(ColumnDescription = "交易备注", Length = 512)]
+    public string? Remark { get; set; }
+}

+ 2 - 2
Admin.NET/Admin.NET.Core/Enum/AlipayCertTypeEnum.cs

@@ -1,7 +1,7 @@
 // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-// 
+//
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-// 
+//
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 namespace Admin.NET.Core;

+ 2 - 2
Admin.NET/Admin.NET.Core/Enum/AlipayIdentityTypeEnum.cs

@@ -1,7 +1,7 @@
 // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-// 
+//
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-// 
+//
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 namespace Admin.NET.Core;

+ 1 - 1
Admin.NET/Admin.NET.Core/EventBus/AppEventSubscriber.cs

@@ -11,7 +11,7 @@ namespace Admin.NET.Core;
 /// </summary>
 public class AppEventSubscriber : IEventSubscriber, ISingleton, IDisposable
 {
-    private readonly static ISugarQueryable<SysTenant> SysTenantQueryable = App.GetService<ISqlSugarClient>().Queryable<SysTenant>();
+    private static readonly ISugarQueryable<SysTenant> SysTenantQueryable = App.GetService<ISqlSugarClient>().Queryable<SysTenant>();
     private readonly IServiceScope _serviceScope;
 
     public AppEventSubscriber(IServiceScopeFactory scopeFactory)

+ 2 - 2
Admin.NET/Admin.NET.Core/Extension/RequestExtension.cs

@@ -1,7 +1,7 @@
 // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-// 
+//
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-// 
+//
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 namespace Admin.NET.Core;

+ 1 - 2
Admin.NET/Admin.NET.Core/GlobalUsings.cs

@@ -6,7 +6,6 @@
 
 global using Admin.NET.Core.Service;
 global using Furion;
-global using Furion.ClayObject;
 global using Furion.ConfigurableOptions;
 global using Furion.DatabaseAccessor;
 global using Furion.DataEncryption;
@@ -15,9 +14,9 @@ global using Furion.DependencyInjection;
 global using Furion.DynamicApiController;
 global using Furion.EventBus;
 global using Furion.FriendlyException;
+global using Furion.HttpRemote;
 global using Furion.JsonSerialization;
 global using Furion.Logging;
-global using Furion.RemoteRequest.Extensions;
 global using Furion.Schedule;
 global using Furion.UnifyResult;
 global using Furion.ViewEngine;

+ 72 - 21
Admin.NET/Admin.NET.Core/Option/AlipayOptions.cs

@@ -4,6 +4,8 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
+using Aop.Api;
+
 namespace Admin.NET.Core;
 
 /// <summary>
@@ -12,57 +14,106 @@ namespace Admin.NET.Core;
 public sealed class AlipayOptions : IConfigurableOptions
 {
     /// <summary>
-    /// 支付宝 APPID(必填)
+    /// 支付宝网关地址
     /// </summary>
-    public string AppId { get; set; }
+    public string ServerUrl { get; init; }
 
     /// <summary>
-    /// 支付宝 websocket 服务地址 (必填)
+    /// 支付宝授权回调地址
     /// </summary>
-    public string AlipayWebsocketUrl { get; set; }
+    public string AuthUrl { get; init; }
 
     /// <summary>
-    /// 支付宝网关地址 (必填)
+    /// 应用授权回调地址
     /// </summary>
-    public string ServerUrl { get; set; }
+    public string AppAuthUrl { get; init; }
 
     /// <summary>
-    /// 支付宝授权回调地址 (必填)
+    /// 支付宝 websocket 服务地址
     /// </summary>
-    public string AuthUrl { get; set; }
+    public string WebsocketUrl { get; init; }
 
     /// <summary>
     /// 应用回调地址
     /// </summary>
-    public string NotifyUrl { get; set; }
+    public string NotifyUrl { get; init; }
 
     /// <summary>
-    /// 加密算法(必填)
+    /// 支付宝根证书存放路径
     /// </summary>
-    public string SignType { get; set; }
+    public string RootCertPath { get; init; }
 
     /// <summary>
-    /// 从支付宝获取敏感信息时的加密密钥(可选)
+    /// 支付宝商户账号列表
+    /// </summary>
+    public List<AlipayMerchantAccount> AccountList { get; init; }
+
+    /// <summary>
+    /// 获取支付宝客户端
+    /// </summary>
+    /// <param name="account"></param>
+    public DefaultAopClient GetClient(AlipayMerchantAccount account)
+    {
+        account = account ?? throw new Exception("未找到支付宝商户账号");
+        string path = App.WebHostEnvironment.ContentRootPath;
+        return new DefaultAopClient(new AlipayConfig
+        {
+            Format = "json",
+            Charset = "UTF-8",
+            ServerUrl = ServerUrl,
+            AppId = account.AppId,
+            SignType = account.SignType,
+            PrivateKey = account.PrivateKey,
+            EncryptKey = account.EncryptKey,
+            RootCertPath = Path.Combine(path, RootCertPath),
+            AppCertPath = Path.Combine(path, account.AppCertPath),
+            AlipayPublicCertPath = Path.Combine(path, account.AlipayPublicCertPath)
+        });
+    }
+}
+
+/// <summary>
+/// 支付宝商户账号信息
+/// </summary>
+public class AlipayMerchantAccount
+{
+    /// <summary>
+    /// 配置Id
     /// </summary>
-    public string EncryptKey { get; set; }
+    public long Id { get; init; }
 
     /// <summary>
-    /// 应用私钥 (必填)
+    /// 商户名称
+    /// </summary>
+    public string Name { get; init; }
+
+    /// <summary>
+    /// 商户AppId
+    /// </summary>
+    public string AppId { get; init; }
+
+    /// <summary>
+    /// 应用私钥
+    /// </summary>
+    public string PrivateKey { get; init; }
+
+    /// <summary>
+    /// 从支付宝获取敏感信息时的加密密钥(可选)
     /// </summary>
-    public string PrivateKey { get; set; }
+    public string EncryptKey { get; init; }
 
     /// <summary>
-    /// 支付宝公钥证书存放路径(证书加签方式必填)
+    /// 加密算法
     /// </summary>
-    public string AlipayPublicCertPath { get; set; }
+    public string SignType { get; init; }
 
     /// <summary>
-    /// 支付宝根证书存放路径(证书加签方式必填)
+    /// 应用公钥证书路径
     /// </summary>
-    public string RootCertPath { get; set; }
+    public string AppCertPath { get; init; }
 
     /// <summary>
-    /// 应用公钥证书存放路径(证书加签方式必填)
+    /// 支付宝公钥证书路径
     /// </summary>
-    public string AppCertPath { get; set; }
+    public string AlipayPublicCertPath { get; init; }
 }

+ 5 - 5
Admin.NET/Admin.NET.Core/Service/Alipay/AlipayErrorCodes.cs

@@ -1,7 +1,7 @@
 // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-// 
+//
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-// 
+//
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 namespace Admin.NET.Core.Service;
@@ -29,8 +29,8 @@ public class AlipayErrorCode
     /// <summary>
     /// 错误码集
     /// </summary>
-    private static readonly List<AlipayErrorCode> StatusCodes = new()
-    {
+    private static readonly List<AlipayErrorCode> StatusCodes =
+    [
         new AlipayErrorCode { Code="SYSTEM_ERROR", Message="系统繁忙", Solution="可能是由于网络或者系统故障,请与技术人员联系以解决该问题。" },
         new AlipayErrorCode { Code="INVALID_PARAMETER", Message="参数有误或没有参数", Solution="请检查并确认查询请求参数合法性。" },
         new AlipayErrorCode { Code="AUTHORISE_NOT_MATCH", Message="授权失败,无法获取用户信息", Solution="检查账户与支付方关系主表关系,确认是否正确配置。" },
@@ -129,7 +129,7 @@ public class AlipayErrorCode
         new AlipayErrorCode { Code="USER_AGREEMENT_VERIFY_FAIL", Message="用户协议校验失败", Solution="确认入参中协议号是否正确" },
         new AlipayErrorCode { Code="USER_NOT_EXIST", Message="用户不存在(仅用于WorldFirst)", Solution="用户不存在,请检查收付款方信息" },
         new AlipayErrorCode { Code="USER_RISK_FREEZE", Message="账户异常被冻结,无法付款,请咨询支付宝客服95188", Solution="账户异常被冻结,无法付款,请咨询支付宝客服95188" }
-    };
+    ];
 
     /// <summary>
     /// 根据错误码获取错误信息

+ 0 - 276
Admin.NET/Admin.NET.Core/Service/Alipay/AlipayService.cs

@@ -1,276 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Aop.Api;
-using Aop.Api.Domain;
-using Aop.Api.Request;
-using Aop.Api.Response;
-using Aop.Api.Util;
-using Microsoft.AspNetCore.Hosting;
-// ReSharper disable All
-
-namespace Admin.NET.Core.Service;
-
-/// <summary>
-/// 支付宝支付服务 🧩
-/// </summary>
-[ApiDescriptionSettings(Order = 240)]
-public class AlipayService : IDynamicApiController, ITransient
-{
-    private readonly IEnumerable<IAlipayNotify> _alipayNotifyList;
-    private readonly IWebHostEnvironment _webHostEnvironment;
-    private readonly SysConfigService _sysConfigService;
-    private readonly IHttpContextAccessor _httpContext;
-    private readonly AlipayOptions _alipayOptions;
-    private readonly IAopClient _alipayClient;
-    private readonly UserManager _userManager;
-
-    public AlipayService(
-        UserManager userManager,
-        IHttpContextAccessor httpContext,
-        SysConfigService sysConfigService,
-        IWebHostEnvironment webHostEnvironment,
-        IOptions<AlipayOptions> alipayOptions)
-    {
-        _userManager = userManager;
-        _httpContext = httpContext;
-        _sysConfigService = sysConfigService;
-        _alipayOptions = alipayOptions.Value;
-        _webHostEnvironment = webHostEnvironment;
-        _alipayNotifyList = App.GetServices<IAlipayNotify>();
-
-        // 初始化支付宝客户端
-        string path = App.WebHostEnvironment.ContentRootPath;
-        _alipayClient = new DefaultAopClient(new AlipayConfig
-        {
-            Format = "json",
-            Charset = "UTF-8",
-            AppId = _alipayOptions.AppId,
-            SignType = _alipayOptions.SignType,
-            ServerUrl = _alipayOptions.ServerUrl,
-            PrivateKey = _alipayOptions.PrivateKey,
-            EncryptKey = _alipayOptions.EncryptKey,
-            AppCertPath = Path.Combine(path, _alipayOptions.AppCertPath),
-            RootCertPath = Path.Combine(path, _alipayOptions.RootCertPath),
-            AlipayPublicCertPath = Path.Combine(path, _alipayOptions.AlipayPublicCertPath)
-        });
-    }
-
-    /// <summary>
-    /// 获取授权信息
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [NonUnify]
-    [AllowAnonymous]
-    [DisplayName("获取授权信息")]
-    [ApiDescriptionSettings(Name = "GetAuthInfo"), HttpGet]
-    public ActionResult GetAuthInfo([FromQuery] AlipayAuthInfoInput input)
-    {
-        var type = input.UserId.Split('-').FirstOrDefault().ToInt();
-        var userId = input.UserId.Split('-').LastOrDefault().ToLong();
-
-        // 当前网页接口地址
-        var currentUrl = $"{_httpContext.HttpContext!.Request.GetOrigin()}{_httpContext.HttpContext!.Request.Path}?userId={input.UserId}";
-        if (string.IsNullOrEmpty(input.AuthCode))
-        {
-            // 重新授权
-            var url = $"{_alipayOptions.AuthUrl}?app_id={_alipayOptions.AppId}&scope=auth_user&redirect_uri={currentUrl}";
-            return new RedirectResult(url);
-        }
-
-        // 组装授权请求参数
-        AlipaySystemOauthTokenRequest request = new()
-        {
-            GrantType = AlipayConst.GrantType,
-            Code = input.AuthCode
-        };
-        AlipaySystemOauthTokenResponse response = _alipayClient.CertificateExecute(request);
-
-        // token换取用户信息
-        AlipayUserInfoShareRequest infoShareRequest = new();
-        AlipayUserInfoShareResponse info = _alipayClient.CertificateExecute(infoShareRequest, response.AccessToken);
-
-        // 循环执行扫码后需要执行的业务逻辑,需要至少一个继承方法返回true,否则抛出异常
-        var pass = false;
-        foreach (var notify in _alipayNotifyList)
-            if (notify.ScanCallback(type, userId, info)) pass = true;
-        if (!pass) throw Oops.Oh("未处理的授权逻辑");
-
-        // 执行完,重定向到指定界面
-        var authPageUrl = _sysConfigService.GetConfigValue<string>(ConfigConst.AlipayAuthPageUrl + type).Result;
-        return new RedirectResult(authPageUrl);
-    }
-
-    /// <summary>
-    /// 支付回调
-    /// </summary>
-    /// <returns></returns>
-    [AllowAnonymous]
-    [DisplayName("支付回调")]
-    [ApiDescriptionSettings(Name = "Notify"), HttpPost]
-    public string Notify()
-    {
-        SortedDictionary<string, string> sorted = new();
-        foreach (string key in _httpContext.HttpContext!.Request.Form.Keys)
-            sorted.Add(key, _httpContext.HttpContext.Request.Form[key]);
-
-        string alipayPublicKey = Path.Combine(_webHostEnvironment.ContentRootPath, _alipayOptions.AlipayPublicCertPath!.Replace('/','\\').TrimStart('\\'));
-        bool signVerified = AlipaySignature.RSACertCheckV1(sorted, alipayPublicKey, "UTF-8", _alipayOptions.SignType); // 调用SDK验证签名
-        if (!signVerified) throw Oops.Oh("交易失败");
-
-        var outTradeNo = sorted.GetValueOrDefault("out_trade_no");
-        try
-        {
-            // 记录回调日志
-            File.AppendAllText($"{_webHostEnvironment.ContentRootPath}\\AlipayLog\\Notify-{DateTime.Today:yyyy-MM-dd}.txt",
-                $"支付宝支付到平台({DateTime.Now:yyyy-MM-dd HH:mm:ss}):{Environment.NewLine} " +
-                $"登录人:{_userManager.UserId}-{_userManager.RealName}{Environment.NewLine} " +
-                $"IP:{App.HttpContext?.GetRemoteIpAddressToIPv4(true)} {Environment.NewLine} " +
-                $"交易号:{outTradeNo}{Environment.NewLine} " +
-                $"参数:{JSON.Serialize(sorted)}{Environment.NewLine} " +
-                $"-----------------------------------------------------------------------------------------------------------------------" +
-                $"{Environment.NewLine}{Environment.NewLine}{Environment.NewLine}");
-        }
-        catch (Exception ex)
-        {
-            Log.Error("支付宝支付回调日志写入失败:", ex);
-        }
-
-        if (sorted.GetValueOrDefault(AlipayConst.TradeStatus) == AlipayConst.TradeSuccess)
-        {
-            // 约定交易码前四位为类型码,后面为订单号
-            var tradeNo = long.Parse(outTradeNo);
-            var type = long.Parse(outTradeNo.Substring(0, 4));
-
-            // 循环执行业务逻辑,若都未处理(回调全部返回false)则交易失败
-            var isError = true;
-            foreach (var notify in _alipayNotifyList)
-                if (notify.TopUpCallback(type, tradeNo)) isError = false;
-            if (isError) throw Oops.Oh("交易失败");
-        }
-        return "success";
-    }
-
-    /// <summary>
-    ///  统一收单下单并支付页面接口
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [DisplayName("统一收单下单并支付页面接口")]
-    [ApiDescriptionSettings(Name = "AlipayTradePagePay"), HttpPost]
-    public string AlipayTradePagePay(AlipayTradePagePayInput input)
-    {
-        AlipayTradeWapPayRequest request = new();
-
-        // 组装业务参数model
-        AlipayTradeWapPayModel model = new()
-        {
-            Subject = input.Subject,
-            OutTradeNo = input.OutTradeNo,
-            TotalAmount = input.TotalAmount,
-            Body = input.Body,
-            ProductCode = "QUICK_WAP_WAY",
-            TimeExpire = input.TimeoutExpress
-        };
-        request.SetBizModel(model);
-
-        // 设置异步通知接收地址
-        request.SetNotifyUrl(_alipayOptions.NotifyUrl);
-
-        var response = _alipayClient.SdkExecute(request);
-        if (response.IsError) throw Oops.Oh(response.SubMsg);
-        return $"{_alipayOptions.ServerUrl}?{response.Body}";
-    }
-
-    /// <summary>
-    ///  交易预创建
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [DisplayName("交易预创建")]
-    [ApiDescriptionSettings(Name = "AlipayPreCreate"), HttpPost]
-    public string AlipayPreCreate(AlipayPreCreateInput input)
-    {
-        AlipayTradePrecreateRequest request = new();
-
-        // 设置异步通知接收地址
-        request.SetNotifyUrl(_alipayOptions.NotifyUrl);
-
-        // 组装业务参数model
-        AlipayTradePrecreateModel model = new()
-        {
-            Subject = input.Subject,
-            OutTradeNo = input.OutTradeNo,
-            TotalAmount = input.TotalAmount,
-            TimeoutExpress = input.TimeoutExpress
-        };
-        request.SetBizModel(model);
-
-        var response = _alipayClient.CertificateExecute(request);
-        if (response.IsError) throw Oops.Oh(response.SubMsg);
-        return response.QrCode;
-    }
-
-    /// <summary>
-    /// 单笔转账到支付宝账户
-    ///  https://opendocs.alipay.com/open/62987723_alipay.fund.trans.uni.transfer
-    /// </summary>
-    [NonAction]
-    public Task<AlipayFundTransUniTransferResponse> Transfer(AlipayFundTransUniTransferInput input)
-    {
-        // 构造请求参数以调用接口
-        AlipayFundTransUniTransferRequest request = new();
-        AlipayFundTransUniTransferModel model = new();
-        model.BizScene = AlipayConst.BizScene;
-        model.ProductCode = AlipayConst.ProductCode;
-
-        // 设置商家侧唯一订单号
-        model.OutBizNo = input.OutBizNo;
-
-        // 设置订单总金额
-        model.TransAmount = input.TransAmount.ToString();
-
-        // 设置转账业务的标题
-        model.OrderTitle = input.OrderTitle;
-
-        // 设置收款方信息
-        Participant payeeInfo = new();
-        payeeInfo.CertType = input.CertType.ToString();
-        payeeInfo.CertNo = input.CertNo;
-        payeeInfo.Identity = input.Identity;
-        payeeInfo.Name = input.Name;
-        payeeInfo.IdentityType = input.IdentityType.ToString();
-        model.PayeeInfo = payeeInfo;
-
-        // 设置业务备注
-        model.Remark = input.Remark;
-
-        // 设置转账业务请求的扩展参数
-        string payerShowNameUseAlias = input.PayerShowNameUseAlias.ToString().ToLower();
-        model.BusinessParams = $"{{\"payer_show_name_use_alias\":\"{payerShowNameUseAlias}\"}}";
-
-        request.SetBizModel(model);
-        var response = _alipayClient.CertificateExecute(request);
-
-        try
-        {
-            File.AppendAllText($"{_webHostEnvironment.ContentRootPath}\\AlipayLog\\{DateTime.Today:yyyy-MM-dd}.txt",
-                $"支付宝付款到账户({DateTime.Now:yyyy-MM-dd HH:mm:ss}):{Environment.NewLine} " +
-                $"登录人:{_userManager.UserId}-{_userManager.RealName}{Environment.NewLine} " +
-                $"IP:{App.HttpContext?.GetRemoteIpAddressToIPv4(true)} {Environment.NewLine} " +
-                $"参数:{JSON.Serialize(model)}{Environment.NewLine} " +
-                $"返回:{JSON.Serialize(response)}{Environment.NewLine}" +
-                $"-----------------------------------------------------------------------------------------------------------------------" +
-                $"{Environment.NewLine}{Environment.NewLine}{Environment.NewLine}");
-        }
-        catch(Exception ex)
-        {
-            Log.Error("单笔转账到支付宝账户日志写入失败:", ex);
-        }
-        return Task.FromResult(response);
-    }
-}

+ 24 - 12
Admin.NET/Admin.NET.Core/Service/Alipay/Dto/AlipayInput.cs

@@ -1,31 +1,40 @@
 // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-// 
+//
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-// 
+//
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 using Aop.Api.Domain;
+using Newtonsoft.Json;
+using System.Text.Json.Serialization;
 
 namespace Admin.NET.Core.Service;
 
 public class AlipayFundTransUniTransferInput
 {
     /// <summary>
-    /// 商家侧唯一订单号
+    /// 用户ID
+    /// </summary>
+    public long UserId { get; set; }
+
+    /// <summary>
+    /// 商户AppId
+    /// </summary>
+    public string AppId { get; set; }
+
+    /// <summary>
+    /// 商家订单号
     /// </summary>
-    [Required(ErrorMessage = "订单号不能为空")]
     public string OutBizNo { get; set; }
 
     /// <summary>
     /// 转账金额
     /// </summary>
-    [Required(ErrorMessage = "转账金额不能为空")]
-    public decimal? TransAmount { get; set; }
+    public decimal TransAmount { get; set; }
 
     /// <summary>
-    /// 转账业务标题
+    /// 业务标题
     /// </summary>
-    [Required(ErrorMessage = "业务标题不能为空")]
     public string OrderTitle { get; set; }
 
     /// <summary>
@@ -46,25 +55,21 @@ public class AlipayFundTransUniTransferInput
     /// <summary>
     /// 收款方证件号码,条件必填
     /// </summary>
-    [CommonValidation($"{nameof(CertType)} != null && string.IsNullOrWhiteSpace({nameof(CertNo)})", "", ErrorMessage = "证件号码不能为空")]
     public string CertNo { get; set; }
 
     /// <summary>
     /// 收款方身份标识
     /// </summary>
-    [Required(ErrorMessage = "身份标识不能为空")]
     public string Identity { get; set; }
 
     /// <summary>
     /// 收款方真实姓名
     /// </summary>
-    [Required(ErrorMessage = "真实姓名不能为空")]
     public string Name { get; set; }
 
     /// <summary>
     /// 收款方身份标识类型
     /// </summary>
-    [Required(ErrorMessage = "身份标识类型不能为空")]
     public AlipayIdentityTypeEnum? IdentityType { get; set; }
 }
 
@@ -159,10 +164,17 @@ public class AlipayAuthInfoInput
     /// <summary>
     /// 用户Id
     /// </summary>
+
+    [JsonProperty("user_id")]
+    [JsonPropertyName("user_id")]
+    [FromQuery(Name = "user_id")]
     public string UserId { get; set; }
 
     /// <summary>
     /// 授权码
     /// </summary>
+    [JsonProperty("auth_code")]
+    [JsonPropertyName("auth_code")]
+    [FromQuery(Name = "auth_code")]
     public string AuthCode { get; set; }
 }

+ 2 - 2
Admin.NET/Admin.NET.Core/Service/Alipay/IAlipayNotify.cs

@@ -1,7 +1,7 @@
 // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-// 
+//
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-// 
+//
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 using Aop.Api.Response;

+ 271 - 0
Admin.NET/Admin.NET.Core/Service/Alipay/SysAlipayService.cs

@@ -0,0 +1,271 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Aop.Api;
+using Aop.Api.Domain;
+using Aop.Api.Request;
+using Aop.Api.Response;
+using Aop.Api.Util;
+using Microsoft.AspNetCore.Hosting;
+using NewLife.Reflection;
+
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 支付宝支付服务 🧩
+/// </summary>
+[ApiDescriptionSettings(Order = 240)]
+public class SysAlipayService : IDynamicApiController, ITransient
+{
+    private readonly IWebHostEnvironment _webHostEnvironment;
+    private readonly SysConfigService _sysConfigService;
+    private readonly List<IAopClient> _alipayClientList;
+    private readonly IHttpContextAccessor _httpContext;
+    private readonly AlipayOptions _option;
+    private readonly ISqlSugarClient _db;
+
+    public SysAlipayService(
+        ISqlSugarClient db,
+        IHttpContextAccessor httpContext,
+        SysConfigService sysConfigService,
+        IWebHostEnvironment webHostEnvironment,
+        IOptions<AlipayOptions> alipayOptions)
+    {
+        _db = db;
+        _httpContext = httpContext;
+        _sysConfigService = sysConfigService;
+        _option = alipayOptions.Value;
+        _webHostEnvironment = webHostEnvironment;
+
+        // 初始化支付宝客户端列表
+        _alipayClientList = [];
+        foreach (var account in _option.AccountList) _alipayClientList.Add(_option.GetClient(account));
+    }
+
+    /// <summary>
+    /// 获取授权信息 🔖
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [NonUnify]
+    [AllowAnonymous]
+    [DisplayName("获取授权信息")]
+    [ApiDescriptionSettings(Name = "AuthInfo"), HttpGet]
+    public ActionResult GetAuthInfo([FromQuery] AlipayAuthInfoInput input)
+    {
+        var type = input.UserId?.Split('-').FirstOrDefault().ToInt();
+        var userId = input.UserId?.Split('-').LastOrDefault().ToLong();
+        var account = _option.AccountList.FirstOrDefault();
+        var alipayClient = _alipayClientList.First();
+
+        // 当前网页接口地址
+        var currentUrl = $"{_option.AppAuthUrl}{_httpContext.HttpContext!.Request.Path}?userId={input.UserId}";
+        if (string.IsNullOrEmpty(input.AuthCode))
+        {
+            // 重新授权
+            var url = $"{_option.AuthUrl}?app_id={account!.AppId}&scope=auth_user&redirect_uri={currentUrl}";
+            return new RedirectResult(url);
+        }
+
+        // 组装授权请求参数
+        AlipaySystemOauthTokenRequest request = new()
+        {
+            GrantType = AlipayConst.GrantType,
+            Code = input.AuthCode
+        };
+        AlipaySystemOauthTokenResponse response = alipayClient.CertificateExecute(request);
+
+        // token换取用户信息
+        AlipayUserInfoShareRequest infoShareRequest = new();
+        AlipayUserInfoShareResponse info = alipayClient.CertificateExecute(infoShareRequest, response.AccessToken);
+
+        // 记录授权信息
+        var entity = _db.Queryable<SysAlipayAuthInfo>().First(u =>
+            (!string.IsNullOrWhiteSpace(u.UserId) && u.UserId == info.UserId) ||
+            (!string.IsNullOrWhiteSpace(u.OpenId) && u.OpenId == info.OpenId)) ?? new();
+        entity.Copy(info, excludes: [nameof(SysAlipayAuthInfo.Gender), nameof(SysAlipayAuthInfo.Age)]);
+        entity.Age = int.Parse(info.Age);
+        entity.Gender = info.Gender switch
+        {
+            "m" => GenderEnum.Male,
+            "f" => GenderEnum.Female,
+            _ => GenderEnum.Unknown
+        };
+        entity.AppId = account!.AppId;
+        if (entity.Id <= 0) _db.Insertable(entity).ExecuteCommand();
+        else _db.Updateable(entity).ExecuteCommand();
+
+        // 执行完,重定向到指定界面
+        //var authPageUrl = _sysConfigService.GetConfigValueByCode<string>(ConfigConst.AlipayAuthPageUrl + type).Result;
+        //return new RedirectResult(authPageUrl);
+        return new RedirectResult(_option.AppAuthUrl + "/index.html");
+    }
+
+    /// <summary>
+    /// 支付回调 🔖
+    /// </summary>
+    /// <returns></returns>
+    [AllowAnonymous]
+    [DisplayName("支付回调")]
+    [ApiDescriptionSettings(Name = "Notify"), HttpPost]
+    public string Notify()
+    {
+        SortedDictionary<string, string> sorted = [];
+        foreach (string key in _httpContext.HttpContext!.Request.Form.Keys)
+            sorted.Add(key, _httpContext.HttpContext.Request.Form[key]);
+
+        var account = _option.AccountList.FirstOrDefault();
+        string alipayPublicKey = Path.Combine(_webHostEnvironment.ContentRootPath, account!.AlipayPublicCertPath!.Replace('/', '\\').TrimStart('\\'));
+        bool signVerified = AlipaySignature.RSACertCheckV1(sorted, alipayPublicKey, "UTF-8", account.SignType); // 调用SDK验证签名
+        if (!signVerified) throw Oops.Oh("交易失败");
+
+        // 更新交易记录
+        var outTradeNo = sorted.GetValueOrDefault("out_trade_no");
+        var transaction = _db.Queryable<SysAlipayTransaction>().First(x => x.OutTradeNo == outTradeNo) ?? throw Oops.Oh("交易记录不存在");
+        transaction.TradeNo = sorted.GetValueOrDefault("trade_no");
+        transaction.TradeStatus = sorted.GetValueOrDefault("trade_status");
+        transaction.FinishTime = sorted.ContainsKey("gmt_payment") ? DateTime.Parse(sorted.GetValueOrDefault("gmt_payment")) : null;
+        transaction.BuyerLogonId = sorted.GetValueOrDefault("buyer_logon_id");
+        transaction.BuyerUserId = sorted.GetValueOrDefault("buyer_user_id");
+        transaction.SellerUserId = sorted.GetValueOrDefault("seller_id");
+        transaction.Remark = sorted.GetValueOrDefault("remark");
+        _db.Updateable(transaction).ExecuteCommand();
+
+        return "success";
+    }
+
+    /// <summary>
+    ///  统一收单下单并支付页面接口 🔖
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [DisplayName("统一收单下单并支付页面接口")]
+    [ApiDescriptionSettings(Name = "AlipayTradePagePay"), HttpPost]
+    public string AlipayTradePagePay(AlipayTradePagePayInput input)
+    {
+        // 创建交易记录,状态为等待支付
+        var transactionRecord = new SysAlipayTransaction
+        {
+            AppId = _option.AccountList.First().AppId,
+            OutTradeNo = input.OutTradeNo,
+            TotalAmount = input.TotalAmount.ToDecimal(),
+            TradeStatus = "WAIT_PAY", // 等待支付
+            CreateTime = DateTime.Now,
+            Subject = input.Subject,
+            Body = input.Body,
+            Remark = "等待用户支付"
+        };
+        _db.Insertable(transactionRecord).ExecuteCommand();
+
+        // 设置支付页面请求,并组装业务参数model,设置异步通知接收地址
+        AlipayTradeWapPayRequest request = new();
+        request.SetBizModel(new AlipayTradeWapPayModel()
+        {
+            Subject = input.Subject,
+            OutTradeNo = input.OutTradeNo,
+            TotalAmount = input.TotalAmount,
+            Body = input.Body,
+            ProductCode = "QUICK_WAP_WAY",
+            TimeExpire = input.TimeoutExpress
+        });
+        request.SetNotifyUrl(_option.NotifyUrl);
+
+        var alipayClient = _alipayClientList.First();
+        var response = alipayClient.SdkExecute(request);
+        if (response.IsError) throw Oops.Oh(response.SubMsg);
+        return $"{_option.ServerUrl}?{response.Body}";
+    }
+
+    /// <summary>
+    ///  交易预创建 🔖
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [DisplayName("交易预创建")]
+    [ApiDescriptionSettings(Name = "AlipayPreCreate"), HttpPost]
+    public string AlipayPreCreate(AlipayPreCreateInput input)
+    {
+        // 创建交易记录,状态为等待支付
+        var transactionRecord = new SysAlipayTransaction
+        {
+            AppId = _option.AccountList.First().AppId,
+            OutTradeNo = input.OutTradeNo,
+            TotalAmount = input.TotalAmount.ToDecimal(),
+            TradeStatus = "WAIT_PAY", // 等待支付
+            CreateTime = DateTime.Now,
+            Subject = input.Subject,
+            Remark = "等待用户支付"
+        };
+        _db.Insertable(transactionRecord).ExecuteCommand();
+
+        // 设置异步通知接收地址,并组装业务参数model
+        AlipayTradePrecreateRequest request = new();
+        request.SetNotifyUrl(_option.NotifyUrl);
+        request.SetBizModel(new AlipayTradePrecreateModel()
+        {
+            Subject = input.Subject,
+            OutTradeNo = input.OutTradeNo,
+            TotalAmount = input.TotalAmount,
+            TimeoutExpress = input.TimeoutExpress
+        });
+
+        var alipayClient = _alipayClientList.First();
+        var response = alipayClient.CertificateExecute(request);
+        if (response.IsError) throw Oops.Oh(response.SubMsg);
+        return response.QrCode;
+    }
+
+    /// <summary>
+    /// 单笔转账到支付宝账户
+    ///  https://opendocs.alipay.com/open/62987723_alipay.fund.trans.uni.transfer
+    /// </summary>
+    [NonAction]
+    public async Task<AlipayFundTransUniTransferResponse> Transfer(AlipayFundTransUniTransferInput input)
+    {
+        var account = _option.AccountList.FirstOrDefault(u => u.AppId == input.AppId) ?? throw Oops.Oh("未找到商户支付宝账号");
+        var alipayClient = _option.GetClient(account);
+
+        // 构造请求参数以调用接口
+        AlipayFundTransUniTransferRequest request = new();
+        AlipayFundTransUniTransferModel model = new()
+        {
+            BizScene = AlipayConst.BizScene,
+            ProductCode = AlipayConst.ProductCode,
+            OutBizNo = input.OutBizNo, // 商家订单
+            TransAmount = $"{input.TransAmount}:F2", // 订单总金额
+            OrderTitle = input.OrderTitle, // 业务标题
+            Remark = input.Remark, // 业务备注
+            PayeeInfo = new() // 收款方信息
+            {
+                CertType = input.CertType?.ToString(),
+                CertNo = input.CertNo,
+                Identity = input.Identity,
+                Name = input.Name,
+                IdentityType = input.IdentityType.ToString()
+            },
+            BusinessParams = input.PayerShowNameUseAlias ? "{\"payer_show_name_use_alias\":\"true\"}" : null
+        };
+
+        request.SetBizModel(model);
+        var response = alipayClient.CertificateExecute(request);
+
+        // 保存转账记录
+        await _db.Insertable(new SysAlipayTransaction
+        {
+            UserId = input.UserId,
+            AppId = input.AppId,
+            TradeNo = response.OrderId,
+            OutTradeNo = input.OutBizNo,
+            TotalAmount = response.Amount.ToDecimal(),
+            TradeStatus = response.Code == "10000" ? "SUCCESS" : "FAILED",
+            Subject = input.OrderTitle,
+            ErrorInfo = response.SubMsg,
+            Remark = input.Remark
+        }).ExecuteCommandAsync();
+
+        return response;
+    }
+}

+ 1 - 2
Admin.NET/Admin.NET.Core/Service/Common/Dto/SysCommonInput.cs

@@ -66,5 +66,4 @@ public class StressTestInput
     /// Query参数
     /// </summary>
     public Dictionary<string, string> QueryParameters { get; set; } = new();
-}
-
+}

+ 1 - 2
Admin.NET/Admin.NET.Core/Service/Common/Dto/SysCommonOutput.cs

@@ -85,5 +85,4 @@ public class StressTestOutput
     /// P999 响应时间(毫秒)
     /// </summary>
     public double Percentile999ResponseTime { get; set; }
-}
-
+}

+ 1 - 2
Admin.NET/Admin.NET.Core/Service/Common/SysCommonService.cs

@@ -4,7 +4,6 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
-using Azure.Core;
 using Microsoft.AspNetCore.Mvc.ApiExplorer;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Utilities.Encoders;
@@ -152,7 +151,7 @@ public class SysCommonService : IDynamicApiController, ITransient
         // 创建一次性的HttpRequestMessage模板
         HttpRequestMessage requestTemplate = CreateRequestMessage(input, fullUri);
 
-        #endregion
+        #endregion 参数构建
 
         var tasks = Enumerable.Range(0, input.NumberOfRounds!.Value * input.NumberOfRequests!.Value).Select(async _ =>
         {

+ 1 - 1
Admin.NET/Admin.NET.Core/Service/Config/SysConfigService.cs

@@ -45,7 +45,7 @@ public class SysConfigService : IDynamicApiController, ITransient
     {
         var queryable = await GetConfigQueryable();
         return await queryable
-            .WhereIF(!_userManager.SuperAdmin,  u => u.SysFlag == YesNoEnum.N)
+            .WhereIF(!_userManager.SuperAdmin, u => u.SysFlag == YesNoEnum.N)
             .WhereIF(!string.IsNullOrWhiteSpace(input.Name?.Trim()), u => u.Name.Contains(input.Name))
             .WhereIF(!string.IsNullOrWhiteSpace(input.Code?.Trim()), u => u.Code.Contains(input.Code))
             .WhereIF(!string.IsNullOrWhiteSpace(input.GroupCode?.Trim()), u => u.GroupCode.Equals(input.GroupCode))

+ 0 - 1
Admin.NET/Admin.NET.Core/Service/Dict/SysDictDataService.cs

@@ -178,7 +178,6 @@ public class SysDictDataService : IDynamicApiController, ITransient
             .ToListAsync();
     }
 
-
     /// <summary>
     /// 根据字典类型Id删除字典值
     /// </summary>

+ 4 - 2
Admin.NET/Admin.NET.Core/Service/Dict/SysDictTypeService.cs

@@ -171,8 +171,10 @@ public class SysDictTypeService : IDynamicApiController, ITransient
             .Select((u, w) => new
             {
                 TypeCode = u.Code,
-                w.Label, w.Value,
-                w.Code, w.TagType,
+                w.Label,
+                w.Value,
+                w.Code,
+                w.TagType,
                 w.StyleSetting,
                 w.ClassSetting,
                 w.ExtData,

+ 16 - 11
Admin.NET/Admin.NET.Core/Service/File/Dto/FileInput.cs

@@ -16,6 +16,11 @@ public class PageFileInput : BasePageInput
     /// </summary>
     public string FileName { get; set; }
 
+    /// <summary>
+    /// 文件路径
+    /// </summary>
+    public string FilePath { get; set; }
+
     /// <summary>
     /// 文件后缀
     /// </summary>
@@ -30,11 +35,6 @@ public class PageFileInput : BasePageInput
     /// 结束时间
     /// </summary>
     public DateTime? EndTime { get; set; }
-
-    /// <summary>
-    /// 租户Id
-    /// </summary>
-    public long TenantId { get; set; }
 }
 
 public class FileInput : BaseIdInput
@@ -56,6 +56,16 @@ public class UploadFileInput
     [Required]
     public IFormFile File { get; set; }
 
+    /// <summary>
+    /// 文件类别
+    /// </summary>
+    public string FileType { get; set; }
+
+    /// <summary>
+    /// 是否公开
+    /// </summary>
+    public bool IsPublic { get; set; } = false;
+
     /// <summary>
     /// 文件路径
     /// </summary>
@@ -70,11 +80,6 @@ public class UploadFileInput
     /// 允许格式:.jpeg.jpg.png.bmp.gif.tif
     /// </summary>
     public string AllowSuffix { get; set; }
-
-    /// <summary>
-    /// 文件类型
-    /// </summary>
-    public string FileType { get; set; }
 }
 
 /// <summary>
@@ -85,7 +90,7 @@ public class UploadFileFromBase64Input
     /// <summary>
     /// 文件名
     /// </summary>
-    public string? FileName { get; set; }
+    public string FileName { get; set; }
 
     /// <summary>
     /// 文件内容

+ 24 - 9
Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs

@@ -50,8 +50,8 @@ public class SysFileService : IDynamicApiController, ITransient
         var privateList = _sysFileRep.AsQueryable().Where(u => u.IsPublic == false);
         // 合并公开和私有附件并分页
         return await _sysFileRep.Context.UnionAll(publicList, privateList)
-            .WhereIF(_userManager.SuperAdmin && input.TenantId > 0, u => u.TenantId == input.TenantId)
             .WhereIF(!string.IsNullOrWhiteSpace(input.FileName), u => u.FileName.Contains(input.FileName.Trim()))
+            .WhereIF(!string.IsNullOrWhiteSpace(input.FilePath), u => u.FilePath.Contains(input.FilePath.Trim()))
             .WhereIF(!string.IsNullOrWhiteSpace(input.StartTime.ToString()) && !string.IsNullOrWhiteSpace(input.EndTime.ToString()),
                 u => u.CreateTime >= input.StartTime && u.CreateTime <= input.EndTime)
             .OrderBy(u => u.CreateTime, OrderByType.Desc)
@@ -123,6 +123,7 @@ public class SysFileService : IDynamicApiController, ITransient
     public async Task<IActionResult> GetPreview([FromRoute] long id)
     {
         var file = await GetFile(id);
+        //var fileName = HttpUtility.UrlEncode(file.FileName, Encoding.GetEncoding("UTF-8"));
         return await GetFileStreamResult(file, file.Id + "");
     }
 
@@ -132,14 +133,14 @@ public class SysFileService : IDynamicApiController, ITransient
     /// <param name="file"></param>
     /// <param name="fileName"></param>
     /// <returns></returns>
-    [NonAction]
-    public async Task<IActionResult> GetFileStreamResult(SysFile file, string fileName)
+    private async Task<IActionResult> GetFileStreamResult(SysFile file, string fileName)
     {
         var filePath = Path.Combine(file.FilePath ?? "", file.Id + file.Suffix);
         if (_OSSProviderOptions.Enabled)
         {
-            var stream = await (await _OSSService.PresignedGetObjectAsync(file.BucketName, filePath, 5)).GetAsStreamAsync();
-            return new FileStreamResult(stream.Stream, "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
+            var httpRemoteService = App.GetRequiredService<IHttpRemoteService>();
+            var stream = await httpRemoteService.GetAsStreamAsync(await _OSSService.PresignedGetObjectAsync(file.BucketName, filePath, 5));
+            return new FileStreamResult(stream, "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
         }
 
         if (App.Configuration["SSHProvider:Enabled"].ToBoolean())
@@ -177,7 +178,7 @@ public class SysFileService : IDynamicApiController, ITransient
         if (App.Configuration["SSHProvider:Enabled"].ToBoolean())
         {
             var sysFile = await _sysFileRep.CopyNew().GetFirstAsync(u => u.Url == url) ?? throw Oops.Oh($"文件不存在");
-            using SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"],
+            using SSHHelper helper = new(App.Configuration["SSHProvider:Host"],
                 App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]);
             return Convert.ToBase64String(helper.ReadAllBytes(sysFile.FilePath));
         }
@@ -259,6 +260,21 @@ public class SysFileService : IDynamicApiController, ITransient
         return file ?? throw Oops.Oh(ErrorCodeEnum.D8000);
     }
 
+    /// <summary>
+    /// 获取文件路径 🔖
+    /// </summary>
+    /// <returns></returns>
+    [DisplayName("获取文件路径")]
+    public async Task<List<TreeNode>> GetFolder()
+    {
+        var files = await _sysFileRep.AsQueryable().ToListAsync();
+        var folders = files.GroupBy(u => u.FilePath).Select(u => u.First().FilePath).ToList();
+
+        var pathTreeBuilder = new PathTreeBuilder();
+        var tree = pathTreeBuilder.BuildTree(folders);
+        return tree.Children;
+    }
+
     /// <summary>
     /// 上传文件 🔖
     /// </summary>
@@ -306,8 +322,7 @@ public class SysFileService : IDynamicApiController, ITransient
 
         // 防止客户端伪造文件类型
         if (!string.IsNullOrWhiteSpace(input.AllowSuffix) && !input.AllowSuffix.Contains(suffix)) throw Oops.Oh(ErrorCodeEnum.D8003);
-        //if (!VerifyFileExtensionName.IsSameType(file.OpenReadStream(), suffix))
-        //    throw Oops.Oh(ErrorCodeEnum.D8001);
+        //if (!VerifyFileExtensionName.IsSameType(file.OpenReadStream(), suffix)) throw Oops.Oh(ErrorCodeEnum.D8001);
 
         // 文件存储位置
         var path = string.IsNullOrWhiteSpace(input.SavePath) ? _uploadOptions.Path : input.SavePath;
@@ -446,7 +461,7 @@ public class SysFileService : IDynamicApiController, ITransient
     }
 
     /// <summary>
-    /// 根据关联查询附件
+    /// 根据关联查询附件 🔖
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>

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

@@ -11,7 +11,7 @@ namespace Admin.NET.Core.Service;
 /// </summary>
 public class JobClusterServer : IJobClusterServer
 {
-    private static readonly SqlSugarRepository<SysJobCluster> SysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
+    private static readonly SqlSugarRepository<SysJobCluster> _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
     private readonly Random _random = new(DateTime.Now.Millisecond);
 
     /// <summary>
@@ -21,13 +21,13 @@ public class JobClusterServer : IJobClusterServer
     public async void Start(JobClusterContext context)
     {
         // 在作业集群表中,如果 clusterId 不存在,则新增一条(否则更新一条),并设置 status 为 ClusterStatus.Waiting
-        if (await SysJobClusterRep.IsAnyAsync(u => u.ClusterId == context.ClusterId))
+        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();
+            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();
+            await _sysJobClusterRep.AsInsertable(new SysJobCluster { ClusterId = context.ClusterId, Status = ClusterStatus.Waiting }).ExecuteCommandAsync();
         }
     }
 
@@ -55,7 +55,7 @@ public class JobClusterServer : IJobClusterServer
                     // 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;
+                    if (await _sysJobClusterRep.IsAnyAsync(u => u.Status == ClusterStatus.Working)) continue;
 
                     await WorkNowAsync(clusterId);
                     return;
@@ -72,7 +72,7 @@ public class JobClusterServer : IJobClusterServer
     public async void Stop(JobClusterContext context)
     {
         // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed
-        await SysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId);
+        await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId);
     }
 
     /// <summary>
@@ -82,7 +82,7 @@ public class JobClusterServer : IJobClusterServer
     public async void Crash(JobClusterContext context)
     {
         // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed
-        await SysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId);
+        await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId);
     }
 
     /// <summary>
@@ -93,6 +93,6 @@ public class JobClusterServer : IJobClusterServer
     private static async Task WorkNowAsync(string clusterId)
     {
         // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Working
-        await SysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Working }, u => u.ClusterId == clusterId);
+        await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Working }, u => u.ClusterId == clusterId);
     }
 }

+ 15 - 8
Admin.NET/Admin.NET.Core/Service/Job/JobMonitor.cs

@@ -11,14 +11,16 @@ namespace Admin.NET.Core.Service;
 /// </summary>
 public class JobMonitor : IJobMonitor
 {
-    private readonly IEventPublisher _eventPublisher;
     private readonly SysConfigService _sysConfigService;
+    private readonly IEventPublisher _eventPublisher;
+    private readonly ILogger<JobMonitor> _logger;
 
-    public JobMonitor(IServiceScopeFactory scopeFactory)
+    public JobMonitor(IServiceScopeFactory serviceScopeFactory, IEventPublisher eventPublisher, ILogger<JobMonitor> logger)
     {
-        var serviceScope = scopeFactory.CreateScope();
+        var serviceScope = serviceScopeFactory.CreateScope();
         _sysConfigService = serviceScope.ServiceProvider.GetRequiredService<SysConfigService>();
-        _eventPublisher = serviceScope.ServiceProvider.GetRequiredService<IEventPublisher>(); ;
+        _eventPublisher = eventPublisher;
+        _logger = logger;
     }
 
     public Task OnExecutingAsync(JobExecutingContext context, CancellationToken stoppingToken)
@@ -28,11 +30,16 @@ public class JobMonitor : IJobMonitor
 
     public async Task OnExecutedAsync(JobExecutedContext context, CancellationToken stoppingToken)
     {
-        // 将异常作业发送到邮件
-        if (await _sysConfigService.GetConfigValue<bool>(ConfigConst.SysErrorMail) && context.Exception != null)
+        if (context.Exception == null) return;
+
+        var exception = $"定时任务【{context.Trigger.Description}】错误:{context.Exception}";
+        // 将作业异常信息记录到本地
+        _logger.LogError(exception);
+
+        if (await _sysConfigService.GetConfigValue<bool>(ConfigConst.SysErrorMail))
         {
-            var errorInfo = $"【{context.Trigger.Description}】定时任务错误:{context.Exception}";
-            await _eventPublisher.PublishAsync(CommonConst.SendErrorMail, errorInfo, stoppingToken);
+            // 将作业异常信息发送到邮件
+            await _eventPublisher.PublishAsync(CommonConst.SendErrorMail, exception, stoppingToken);
         }
     }
 }

+ 12 - 1
Admin.NET/Admin.NET.Core/Service/Job/SysJobService.cs

@@ -9,7 +9,7 @@ namespace Admin.NET.Core.Service;
 /// <summary>
 /// 系统作业任务服务 🧩
 /// </summary>
-[ApiDescriptionSettings(Order = 320)]
+[ApiDescriptionSettings(Order = 320, Description = "作业任务")]
 public class SysJobService : IDynamicApiController, ITransient
 {
     private readonly SqlSugarRepository<SysJobDetail> _sysJobDetailRep;
@@ -356,4 +356,15 @@ public class SysJobService : IDynamicApiController, ITransient
             .OrderByDescending(u => u.Id)
             .ToPagedListAsync(input.Page, input.PageSize);
     }
+
+    /// <summary>
+    /// 清空作业触发器运行记录 🔖
+    /// </summary>
+    /// <returns></returns>
+    [ApiDescriptionSettings(Name = "ClearJobTriggerRecord"), HttpPost]
+    [DisplayName("清空作业触发器运行记录")]
+    public void ClearJobTriggerRecord()
+    {
+        _sysJobTriggerRecordRep.AsSugarClient().DbMaintenance.TruncateTable<SysJobTriggerRecord>();
+    }
 }

+ 3 - 3
Admin.NET/Admin.NET.Core/Service/Message/SysSmsService.cs

@@ -76,10 +76,10 @@ public class SysSmsService : IDynamicApiController, ITransient
         var random = new Random();
         var verifyCode = random.Next(100000, 999999);
 
-        var templateParam = Clay.Object(new
+        var templateParam = new
         {
             code = verifyCode
-        });
+        };
 
         var client = CreateAliyunClient();
         var template = _smsOptions.Aliyun.GetTemplate();
@@ -88,7 +88,7 @@ public class SysSmsService : IDynamicApiController, ITransient
             PhoneNumbers = phoneNumber, // 待发送手机号, 多个以逗号分隔
             SignName = template.SignName, // 短信签名
             TemplateCode = template.TemplateCode, // 短信模板
-            TemplateParam = templateParam.ToString(), // 模板中的变量替换JSON串
+            TemplateParam = templateParam.ToJson(), // 模板中的变量替换JSON串
             OutId = YitIdHelper.NextId().ToString()
         };
         var sendSmsResponse = await client.SendSmsAsync(sendSmsRequest);

+ 6 - 4
Admin.NET/Admin.NET.Core/Service/Server/SysServerService.cs

@@ -9,7 +9,7 @@ namespace Admin.NET.Core.Service;
 /// <summary>
 /// 系统服务器监控服务 🧩
 /// </summary>
-[ApiDescriptionSettings(Order = 290)]
+[ApiDescriptionSettings(Order = 290, Description = "服务器监控")]
 public class SysServerService : IDynamicApiController, ITransient
 {
     public SysServerService()
@@ -31,8 +31,8 @@ public class SysServerService : IDynamicApiController, ITransient
             ProcessorCount = Environment.ProcessorCount + " 核", // CPU核心数
             SysRunTime = ComputerUtil.GetRunTime(), // 系统运行时间
             RemoteIp = ComputerUtil.GetIpFromOnline(), // 外网地址
-            LocalIp = App.HttpContext?.Connection.LocalIpAddress!.MapToIPv4().ToString(), // 本地地址
-            RuntimeInformation.FrameworkDescription, // NET框架
+            LocalIp = App.HttpContext?.Connection?.LocalIpAddress!.MapToIPv4().ToString(), // 本地地址
+            FrameworkDescription = RuntimeInformation.FrameworkDescription + " / " + App.GetOptions<DbConnectionOptions>().ConnectionConfigs[0].DbType.ToString(), // NET框架 + 数据库类型
             Environment = App.HostEnvironment.IsDevelopment() ? "Development" : "Production",
             Wwwroot = App.WebHostEnvironment.WebRootPath, // 网站根目录
             Stage = App.HostEnvironment.IsStaging() ? "Stage环境" : "非Stage环境", // 是否Stage环境
@@ -58,7 +58,7 @@ public class SysServerService : IDynamicApiController, ITransient
             memoryMetrics.UsedRam, // 已用内存
             memoryMetrics.TotalRam, // 总内存
             memoryMetrics.RamRate, // 内存使用率
-            memoryMetrics.CpuRate, // Cpu使用率
+            memoryMetrics.CpuRates, // Cpu使用率
             StartTime = programStartTime.ToString("yyyy-MM-dd HH:mm:ss"), // 服务启动时间
             RunTime = programRunTime, // 服务运行时间
         };
@@ -109,6 +109,7 @@ public class SysServerService : IDynamicApiController, ITransient
         var giteeAuthenticationOptionsAssembly = typeof(AspNet.Security.OAuth.Gitee.GiteeAuthenticationOptions).Assembly.GetName();
         var hashidsAssembly = typeof(HashidsNet.Hashids).Assembly.GetName();
         var sftpClientAssembly = typeof(Renci.SshNet.SftpClient).Assembly.GetName();
+        var hardwareInfoAssembly = typeof(Hardware.Info.HardwareInfo).Assembly.GetName();
 
         return new[]
         {
@@ -140,6 +141,7 @@ public class SysServerService : IDynamicApiController, ITransient
             new { giteeAuthenticationOptionsAssembly.Name, giteeAuthenticationOptionsAssembly.Version },
             new { hashidsAssembly.Name, hashidsAssembly.Version },
             new { sftpClientAssembly.Name, sftpClientAssembly.Version },
+            new { hardwareInfoAssembly.Name, hardwareInfoAssembly.Version },
         };
     }
 }

+ 2 - 2
Admin.NET/Admin.NET.Core/Service/Template/Dto/SysTemplateInput.cs

@@ -1,7 +1,7 @@
 // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-// 
+//
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-// 
+//
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 namespace Admin.NET.Core.Service;

+ 0 - 1
Admin.NET/Admin.NET.Core/Service/Template/Dto/SysTemplateOutput.cs

@@ -8,5 +8,4 @@ namespace Admin.NET.Core.Service;
 
 public class SysTemplateOutput
 {
-
 }

+ 2 - 0
Admin.NET/Admin.NET.Core/SignatureAuth/SignatureAuthenticationHandler.cs

@@ -27,12 +27,14 @@ public sealed class SignatureAuthenticationHandler : AuthenticationHandler<Signa
     }
 
 #else
+
     public SignatureAuthenticationHandler(IOptionsMonitor<SignatureAuthenticationOptions> options,
         ILoggerFactory logger,
         UrlEncoder encoder)
         : base(options, logger, encoder)
     {
     }
+
 #endif
 
     private readonly SysCacheService _sysCacheService = App.GetRequiredService<SysCacheService>();

+ 1 - 0
Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarFilter.cs

@@ -12,6 +12,7 @@ public static class SqlSugarFilter
     /// 缓存全局查询过滤器(内存缓存)
     /// </summary>
     private static readonly ICache Cache = NewLife.Caching.Cache.Default;
+
     private static readonly SysOrgService SysOrgService = App.GetRequiredService<SysOrgService>();
     private static readonly SysCacheService SysCacheService = App.GetRequiredService<SysCacheService>();
 

+ 8 - 6
Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs

@@ -385,13 +385,15 @@ public static class SqlSugarSetup
                 return entityType.GetCustomAttributes<SysTableAttribute>().Any() ||
                        (!entityType.GetCustomAttributes<LogTableAttribute>().Any() &&
                         !entityType.GetCustomAttributes<TenantAttribute>().Any());
+
             case SqlSugarConst.LogConfigId:
                 return entityType.GetCustomAttributes<LogTableAttribute>().Any();
+
             default:
-            {
-                var tenantAttribute = entityType.GetCustomAttribute<TenantAttribute>();
-                return tenantAttribute != null && tenantAttribute.configId.ToString() == config.ConfigId.ToString();
-            }
+                {
+                    var tenantAttribute = entityType.GetCustomAttribute<TenantAttribute>();
+                    return tenantAttribute != null && tenantAttribute.configId.ToString() == config.ConfigId.ToString();
+                }
         }
     }
 
@@ -406,7 +408,7 @@ public static class SqlSugarSetup
         int count = 0, sum = entityTypes.Count;
         var tasks = entityTypes.Select(entityType => Task.Run(() =>
         {
-            Console.WriteLine($"初始化表结构 {entityType.FullName, -64} ({config.ConfigId} - {Interlocked.Increment(ref count):D003}/{sum:D003})");
+            Console.WriteLine($"初始化表结构 {entityType.FullName,-64} ({config.ConfigId} - {Interlocked.Increment(ref count):D003}/{sum:D003})");
             UpdateNullableColumns(dbProvider, entityType);
             InitializeTable(dbProvider, entityType);
         }));
@@ -569,7 +571,7 @@ public static class SqlSugarSetup
                     dbProvider.InsertableByObject(dataList).ExecuteCommand();
                 }
             }
-            Console.WriteLine($"添加数据 {entityInfo.DbTableName, -32} ({config.ConfigId} - {Interlocked.Increment(ref count):D003}/{sum:D003},数据量:{dataList.Count:D003},插入 {insertCount:D003} 条记录,修改 {updateCount:D003} 条记录)");
+            Console.WriteLine($"添加数据 {entityInfo.DbTableName,-32} ({config.ConfigId} - {Interlocked.Increment(ref count):D003}/{sum:D003},数据量:{dataList.Count:D003},插入 {insertCount:D003} 条记录,修改 {updateCount:D003} 条记录)");
         }
     }
 

+ 2 - 1
Admin.NET/Admin.NET.Core/Utils/CommonUtil.cs

@@ -419,7 +419,8 @@ public static class CommonUtil
                     .Where((u, a) => u.Status == StatusEnum.Enable && a.Status == StatusEnum.Enable)
                     .Select((u, a) => new
                     {
-                        a.Label, a.Value
+                        a.Label,
+                        a.Value
                     }).ToList()
                     .ToDictionary(u => u.Value.ParseTo(targetProp.PropertyType), u => u.Label);
                 propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>(mappingValues, targetProp, propertyInfo));

+ 114 - 20
Admin.NET/Admin.NET.Core/Utils/ComputerUtil.cs

@@ -31,7 +31,11 @@ public static class ComputerUtil
         memoryMetrics.UsedRam = Math.Round(memoryMetrics.Used / 1024, 2) + "GB";
         memoryMetrics.TotalRam = Math.Round(memoryMetrics.Total / 1024, 2) + "GB";
         memoryMetrics.RamRate = Math.Ceiling(100 * memoryMetrics.Used / memoryMetrics.Total) + "%";
-        memoryMetrics.CpuRate = Math.Ceiling(GetCPURate().ParseToDouble()) + "%";
+        var cpuRates = GetCPURates();
+        if (cpuRates != null)
+        {
+            memoryMetrics.CpuRates = cpuRates.Select(u => Math.Ceiling(u.ParseToDouble()) + "%").ToList();
+        }
         return memoryMetrics;
     }
 
@@ -124,16 +128,16 @@ public static class ComputerUtil
             foreach (var item in driveList)
             {
                 if (item.DriveType == DriveType.CDRom) continue;
-                var obj = new DiskInfo()
+                var diskInfo = new DiskInfo()
                 {
                     DiskName = item.Name,
                     TypeName = item.DriveType.ToString(),
                     TotalSize = Math.Round(item.TotalSize / 1024 / 1024 / 1024.0m, 2, MidpointRounding.AwayFromZero),
                     AvailableFreeSpace = Math.Round(item.AvailableFreeSpace / 1024 / 1024 / 1024.0m, 2, MidpointRounding.AwayFromZero),
                 };
-                obj.Used = obj.TotalSize - obj.AvailableFreeSpace;
-                obj.AvailablePercent = decimal.Ceiling(obj.Used / (decimal)obj.TotalSize * 100);
-                diskInfos.Add(obj);
+                diskInfo.Used = diskInfo.TotalSize - diskInfo.AvailableFreeSpace;
+                diskInfo.AvailablePercent = decimal.Ceiling(diskInfo.Used / (decimal)diskInfo.TotalSize * 100);
+                diskInfos.Add(diskInfo);
             }
         }
         return diskInfos;
@@ -148,7 +152,8 @@ public static class ComputerUtil
         try
         {
             var url = "https://www.ip.cn/api/index?ip&type=0";
-            var str = url.GetAsStringAsync().GetAwaiter().GetResult();
+            var httpRemoteService = App.GetRequiredService<IHttpRemoteService>();
+            var str = httpRemoteService.GetAsStringAsync(url).GetAwaiter().GetResult();
             var resp = JSON.Deserialize<IpCnResp>(str);
             return resp.Ip + " " + resp.Address;
         }
@@ -168,25 +173,25 @@ public static class ComputerUtil
         return RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
     }
 
-    public static string GetCPURate()
+    public static List<string> GetCPURates()
     {
-        string cpuRate;
+        var cpuRates = new List<string>();
         if (IsMacOS())
         {
             string output = ShellUtil.Bash("top -l 1 | grep \"CPU usage\" | awk '{print $3 + $5}'");
-            cpuRate = output.Trim();
+            cpuRates.Add(output.Trim());
         }
         else if (IsUnix())
         {
             string output = ShellUtil.Bash("awk '{u=$2+$4; t=$2+$4+$5; if (NR==1){u1=u; t1=t;} else print ($2+$4-u1) * 100 / (t-t1); }' <(grep 'cpu ' /proc/stat) <(sleep 1;grep 'cpu ' /proc/stat)");
-            cpuRate = output.Trim();
+            cpuRates.Add(output.Trim());
         }
         else
         {
             string output = ShellUtil.Cmd("wmic", "cpu get LoadPercentage");
-            cpuRate = output.Replace("LoadPercentage", string.Empty).Trim();
+            cpuRates.AddRange(output.Replace("LoadPercentage", string.Empty).Trim().Split("\r\r\n"));
         }
-        return cpuRate;
+        return cpuRates;
     }
 
     /// <summary>
@@ -198,10 +203,10 @@ public static class ComputerUtil
         string runTime = string.Empty;
         if (IsMacOS())
         {
-            //macOS 获取系统启动时间:
-            //sysctl -n kern.boottime | awk '{print $4}' | tr -d ','
-            //返回:1705379131
-            //使用date格式化即可
+            // macOS 获取系统启动时间:
+            // sysctl -n kern.boottime | awk '{print $4}' | tr -d ','
+            // 返回:1705379131
+            // 使用date格式化即可
             string output = ShellUtil.Bash("date -r $(sysctl -n kern.boottime | awk '{print $4}' | tr -d ',') +\"%Y-%m-%d %H:%M:%S\"").Trim();
             runTime = DateTimeUtil.FormatTime((DateTime.Now - output.ParseToDateTime()).TotalMilliseconds.ToString().Split('.')[0].ParseToLong());
         }
@@ -256,7 +261,7 @@ public class MemoryMetrics
     /// <summary>
     /// CPU使用率%
     /// </summary>
-    public string CpuRate { get; set; }
+    public List<string> CpuRates { get; set; }
 
     /// <summary>
     /// 总内存 GB
@@ -326,7 +331,7 @@ public class MemoryMetricsClient
         string output = ShellUtil.Cmd("wmic", "OS get FreePhysicalMemory,TotalVisibleMemorySize /Value");
         var metrics = new MemoryMetrics();
         var lines = output.Trim().Split('\n', (char)StringSplitOptions.RemoveEmptyEntries);
-        if (lines.Length <= 0) return metrics;
+        if (lines.Length <= 1) return metrics;
 
         var freeMemoryParts = lines[0].Split('=', (char)StringSplitOptions.RemoveEmptyEntries);
         var totalMemoryParts = lines[1].Split('=', (char)StringSplitOptions.RemoveEmptyEntries);
@@ -352,7 +357,6 @@ public class MemoryMetricsClient
         metrics.Total = double.Parse(memory[0]) / 1024;
         metrics.Free = double.Parse(memory[1]) / 1024;
         metrics.Used = metrics.Total - metrics.Free;
-
         return metrics;
     }
 
@@ -403,7 +407,78 @@ public class ShellUtil
     }
 
     /// <summary>
-    /// windows系统命令
+    /// windows CMD 系统命令
+    /// </summary>
+    /// <param name="fileName"></param>
+    /// <param name="args"></param>
+    /// <returns></returns>
+    public static string Cmd(string fileName, string args)
+    {
+        var info = new ProcessStartInfo
+        {
+            FileName = fileName,
+            Arguments = args,
+            RedirectStandardOutput = true
+        };
+
+        var output = string.Empty;
+        using (var process = Process.Start(info))
+        {
+            output = process.StandardOutput.ReadToEnd();
+        }
+        return output;
+    }
+
+    /// <summary>
+    /// Windows POWERSHELL 系统命令
+    /// </summary>
+    /// <param name="script"></param>
+    /// <returns></returns>
+    public static string PowerShell(string script)
+    {
+        using var PowerShellInstance = System.Management.Automation.PowerShell.Create();
+        PowerShellInstance.AddScript(script);
+        var PSOutput = PowerShellInstance.Invoke();
+
+        var output = new StringBuilder();
+        foreach (var outputItem in PSOutput)
+        {
+            output.AppendLine(outputItem.BaseObject.ToString());
+        }
+        return output.ToString();
+    }
+}
+
+public class ShellHelper
+{
+    /// <summary>
+    /// Linux 系统命令
+    /// </summary>
+    /// <param name="command"></param>
+    /// <returns></returns>
+    public static string Bash(string command)
+    {
+        var escapedArgs = command.Replace("\"", "\\\"");
+        var process = new Process()
+        {
+            StartInfo = new ProcessStartInfo
+            {
+                FileName = "/bin/bash",
+                Arguments = $"-c \"{escapedArgs}\"",
+                RedirectStandardOutput = true,
+                UseShellExecute = false,
+                CreateNoWindow = true,
+            }
+        };
+        process.Start();
+        string result = process.StandardOutput.ReadToEnd();
+        process.WaitForExit();
+        process.Dispose();
+        return result;
+    }
+
+    /// <summary>
+    /// Windows CMD 系统命令
     /// </summary>
     /// <param name="fileName"></param>
     /// <param name="args"></param>
@@ -424,4 +499,23 @@ public class ShellUtil
         }
         return output;
     }
+
+    /// <summary>
+    /// Windows POWERSHELL 系统命令
+    /// </summary>
+    /// <param name="script"></param>
+    /// <returns></returns>
+    public static string PowerShell(string script)
+    {
+        using var PowerShellInstance = System.Management.Automation.PowerShell.Create();
+        PowerShellInstance.AddScript(script);
+        var PSOutput = PowerShellInstance.Invoke();
+
+        var output = new StringBuilder();
+        foreach (var outputItem in PSOutput)
+        {
+            output.AppendLine(outputItem.BaseObject.ToString());
+        }
+        return output.ToString();
+    }
 }

+ 57 - 0
Admin.NET/Admin.NET.Core/Utils/PathTreeBuilder.cs

@@ -0,0 +1,57 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 树形节点
+/// </summary>
+public class TreeNode
+{
+    public int Id { get; set; }
+    public int Pid { get; set; }
+    public string Name { get; set; }
+    public List<TreeNode> Children { get; set; } = new();
+}
+
+/// <summary>
+/// 根据路径数组生成树结构
+/// </summary>
+public class PathTreeBuilder
+{
+    private int _nextId = 1;
+
+    public TreeNode BuildTree(List<string> paths)
+    {
+        var root = new TreeNode { Id = 1, Pid = 0, Name = "文件目录" }; // 根节点
+        var dict = new Dictionary<string, TreeNode>();
+
+        foreach (var path in paths)
+        {
+            var parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
+            TreeNode currentNode = root;
+
+            foreach (var part in parts)
+            {
+                var key = currentNode.Id + "_" + part; // 生成唯一键
+                if (!dict.ContainsKey(key))
+                {
+                    var newNode = new TreeNode
+                    {
+                        Id = _nextId++,
+                        Pid = currentNode.Id,
+                        Name = part
+                    };
+                    currentNode.Children.Add(newNode);
+                    dict[key] = newNode;
+                }
+                currentNode = dict[key]; // 更新当前节点
+            }
+        }
+
+        return root;
+    }
+}

+ 2 - 2
Admin.NET/Admin.NET.Core/Utils/SafeMath.cs

@@ -1,7 +1,7 @@
 // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-// 
+//
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-// 
+//
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 using System.Globalization;

+ 2 - 2
Admin.NET/Admin.NET.Test/Admin.NET.Test.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
+        <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
         <NoWarn>1701;1702;1591;8632</NoWarn>
         <DocumentationFile></DocumentationFile>
         <ImplicitUsings>enable</ImplicitUsings>
@@ -12,7 +12,7 @@
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="Furion.Xunit" Version="4.9.5.26" />
+      <PackageReference Include="Furion.Xunit" Version="4.9.7.3" />
       <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
       <PackageReference Include="Selenium.Support" Version="4.27.0" />
       <PackageReference Include="Selenium.WebDriver" Version="4.27.0" />

+ 3 - 3
Admin.NET/Admin.NET.Test/BaseTest.cs

@@ -1,7 +1,7 @@
 // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-// 
+//
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-// 
+//
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 using OpenQA.Selenium;
@@ -16,7 +16,7 @@ namespace Admin.NET.Test;
 public class BaseTest : IDisposable
 {
     private readonly string _baseUrl = "http://localhost:8888";
-    protected readonly EdgeDriver Driver = new ();
+    protected readonly EdgeDriver Driver = new();
 
     protected BaseTest(string token = null)
     {

+ 0 - 1
Admin.NET/Admin.NET.Test/User/UserTest.cs

@@ -38,7 +38,6 @@ public class UserTest : BaseTest
         await Task.Delay(1000);
         Driver.FindElement(By.Id("tab-0")).Click();
 
-
         var tab = Driver.FindElement(By.Id("pane-0"));
         var formItemList = tab.FindElements(By.CssSelector("input"));
 

+ 2 - 2
Admin.NET/Admin.NET.Test/Utils/DateTimeUtilTests.cs

@@ -1,7 +1,7 @@
 // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-// 
+//
 // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-// 
+//
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 using Admin.NET.Core;

+ 2 - 2
Admin.NET/Admin.NET.Web.Core/Admin.NET.Web.Core.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
     <NoWarn>1701;1702;1591</NoWarn>
     <DocumentationFile></DocumentationFile>
     <GenerateDocumentationFile>True</GenerateDocumentationFile>
@@ -11,7 +11,7 @@
 
   <ItemGroup>
     <PackageReference Include="IGeekFan.AspNetCore.Knife4jUI" Version="0.0.16" />
-    <PackageReference Include="System.Security.Cryptography.Pkcs" Version="9.0.0" />
+    <PackageReference Include="System.Security.Cryptography.Pkcs" Version="9.0.1" />
   </ItemGroup>
 
   <ItemGroup>

+ 1 - 1
Admin.NET/Admin.NET.Web.Core/Startup.cs

@@ -67,7 +67,7 @@ public class Startup : AppStartup
         // 允许跨域
         services.AddCorsAccessor();
         // 远程请求
-        services.AddRemoteRequest();
+        services.AddHttpRemote();
         // 任务队列
         services.AddTaskQueue();
         // 任务调度

+ 1 - 1
Admin.NET/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
-    <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
     <ImplicitUsings>enable</ImplicitUsings>
     <SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
     <PublishReadyToRunComposite>true</PublishReadyToRunComposite>

+ 1 - 1
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Admin.NET.Plugin.ApprovalFlow.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
     <NoWarn>1701;1702;1591;8632</NoWarn>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>disable</Nullable>

+ 1 - 1
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Const/ApprovalFlowConst.cs

@@ -15,5 +15,5 @@ public class ApprovalFlowConst
     /// <summary>
     /// API分组名称
     /// </summary>
-    public const string GroupName = "ApprovalFlow";
+    public const string GroupName = "审批流程";
 }

+ 6 - 6
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Entity/ApprovalFlow.cs

@@ -17,37 +17,37 @@ public class ApprovalFlow : EntityBaseData
     /// </summary>
     [SugarColumn(ColumnDescription = "编号", Length = 32)]
     [MaxLength(32)]
-    public virtual string? Code { get; set; }
+    public string? Code { get; set; }
 
     /// <summary>
     /// 名称
     /// </summary>
     [SugarColumn(ColumnDescription = "名称", Length = 32)]
     [MaxLength(32)]
-    public virtual string Name { get; set; }
+    public string Name { get; set; }
 
     /// <summary>
     /// 表单
     /// </summary>
     [SugarColumn(ColumnDescription = "表单", ColumnDataType = StaticConfig.CodeFirst_BigString)]
-    public virtual string? FormJson { get; set; }
+    public string? FormJson { get; set; }
 
     /// <summary>
     /// 流程
     /// </summary>
     [SugarColumn(ColumnDescription = "流程", ColumnDataType = StaticConfig.CodeFirst_BigString)]
-    public virtual string? FlowJson { get; set; }
+    public string? FlowJson { get; set; }
 
     /// <summary>
     /// 状态
     /// </summary>
     [SugarColumn(ColumnDescription = "状态")]
-    public virtual int? Status { get; set; }
+    public int? Status { get; set; }
 
     /// <summary>
     /// 备注
     /// </summary>
     [SugarColumn(ColumnDescription = "备注", Length = 256)]
     [MaxLength(256)]
-    public virtual string? Remark { get; set; }
+    public string? Remark { get; set; }
 }

+ 50 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Entity/ApprovalFlowRecord.cs

@@ -0,0 +1,50 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.ApprovalFlow;
+
+/// <summary>
+/// 审批流流程记录
+/// </summary>
+[SugarTable(null, "审批流流程记录")]
+public class ApprovalFlowRecord : EntityBaseData
+{
+    /// <summary>
+    /// 表单名称
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单名称", Length = 255)]
+    public string? FormName { get; set; }
+
+    /// <summary>
+    /// 表单状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单状态", Length = 32)]
+    public string? FormStatus { get; set; }
+
+    /// <summary>
+    /// 表单触发
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单触发", ColumnDataType = StaticConfig.CodeFirst_BigString)]
+    public string? FormJson { get; set; }
+
+    /// <summary>
+    /// 表单结果
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单结果", ColumnDataType = StaticConfig.CodeFirst_BigString)]
+    public string? FormResult { get; set; }
+
+    /// <summary>
+    /// 流程结构
+    /// </summary>
+    [SugarColumn(ColumnDescription = "流程结构", ColumnDataType = StaticConfig.CodeFirst_BigString)]
+    public string? FlowJson { get; set; }
+
+    /// <summary>
+    /// 流程结果
+    /// </summary>
+    [SugarColumn(ColumnDescription = "流程结果", ColumnDataType = StaticConfig.CodeFirst_BigString)]
+    public string? FlowResult { get; set; }
+}

+ 62 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Entity/ApprovalForm.cs

@@ -0,0 +1,62 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.ApprovalFlow;
+
+/// <summary>
+/// 审批流表单
+/// </summary>
+[SugarTable(null, "审批流表单")]
+public class ApprovalForm : EntityBaseData
+{
+    /// <summary>
+    /// 编号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "编号", Length = 32)]
+    public string? Code { get; set; }
+
+    /// <summary>
+    /// 名称
+    /// </summary>
+    [SugarColumn(ColumnDescription = "名称", Length = 32)]
+    public string? Name { get; set; }
+
+    /// <summary>
+    /// 表单名称
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单名称", Length = 32)]
+    public string? FormName { get; set; }
+
+    /// <summary>
+    /// 表单属性
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单属性", Length = 32)]
+    public string? FormType { get; set; }
+
+    /// <summary>
+    /// 表单状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单状态")]
+    public int? FormStatus { get; set; }
+
+    /// <summary>
+    /// 表单结果
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单结果", ColumnDataType = StaticConfig.CodeFirst_BigString)]
+    public string? FormResult { get; set; }
+
+    /// <summary>
+    /// 状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "状态")]
+    public int? Status { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    [SugarColumn(ColumnDescription = "备注", Length = 255)]
+    public string? Remark { get; set; }
+}

+ 56 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Entity/ApprovalFormRecord.cs

@@ -0,0 +1,56 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.ApprovalFlow;
+
+/// <summary>
+/// 审批流表单记录
+/// </summary>
+[SugarTable(null, "审批流表单记录")]
+public class ApprovalFormRecord : EntityBaseData
+{
+    /// <summary>
+    /// 流程Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "流程Id")]
+    public long? FlowId { get; set; }
+
+    /// <summary>
+    /// 表单名称
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单名称", Length = 32)]
+    public string? FormName { get; set; }
+
+    /// <summary>
+    /// 表单类型
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单类型", Length = 32)]
+    public string? FormType { get; set; }
+
+    /// <summary>
+    /// 表单状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单状态", Length = 11)]
+    public string? FormStatus { get; set; }
+
+    /// <summary>
+    /// 修改前
+    /// </summary>
+    [SugarColumn(ColumnDescription = "修改前", ColumnDataType = StaticConfig.CodeFirst_BigString)]
+    public string? FormBefore { get; set; }
+
+    /// <summary>
+    /// 修改后
+    /// </summary>
+    [SugarColumn(ColumnDescription = "修改后", ColumnDataType = StaticConfig.CodeFirst_BigString)]
+    public string? FormAfter { get; set; }
+
+    /// <summary>
+    /// 表单结果
+    /// </summary>
+    [SugarColumn(ColumnDescription = "表单结果", ColumnDataType = StaticConfig.CodeFirst_BigString)]
+    public string? FormResult { get; set; }
+}

+ 0 - 2
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Enum/FlowTypeEnum.cs

@@ -4,8 +4,6 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
-using System.ComponentModel;
-
 namespace Admin.NET.Plugin.ApprovalFlow;
 
 /// <summary>

+ 1 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/GlobalUsings.cs

@@ -15,5 +15,6 @@ global using Microsoft.Extensions.DependencyInjection;
 global using SqlSugar;
 global using System;
 global using System.Collections.Generic;
+global using System.ComponentModel;
 global using System.ComponentModel.DataAnnotations;
 global using System.Threading.Tasks;

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

@@ -4,6 +4,7 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
+using Admin.NET.Plugin.ApprovalFlow.Service;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Http;
 
@@ -31,16 +32,19 @@ public static class ApprovalFlowMiddlewareExtensions
 public class ApprovalFlowMiddleware
 {
     private readonly RequestDelegate _next;
+    private readonly SysApprovalService _sysApprovalService;
 
     public ApprovalFlowMiddleware(RequestDelegate next)
     {
         _next = next;
+        _sysApprovalService = App.GetRequiredService<SysApprovalService>();
     }
 
-    public async Task Invoke(HttpContext context)
+    public async Task InvokeAsync(HttpContext context)
     {
-        // await App.GetRequiredService<SysApprovalService>().MatchApproval(context);
+        await _sysApprovalService.MatchApproval(context);
 
-        await _next.Invoke(context);
+        // 调用下一个中间件
+        await _next(context);
     }
 }

+ 1 - 2
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/SeedData/SysMenuSeedData.cs

@@ -17,10 +17,9 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
     /// <returns></returns>
     public IEnumerable<SysMenu> HasData()
     {
-        var system = new Admin.NET.Core.SysMenuSeedData().HasData().First(u => u.Type == MenuTypeEnum.Dir && u.Title == "系统管理");
         return new[]
         {
-            new SysMenu{ Id=1309000000001, Pid=system.Id, Title="审批流程", Path="/platform/approvalFlow", Name="approvalFlow", Component="/approvalFlow/index", Icon="ele-Help", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=2000 },
+            new SysMenu{ Id=1320000000001, Pid=1310000000301, Title="审批流程", Path="/platform/approvalFlow", Name="approvalFlow", Component="/approvalFlow/index", Icon="ele-Help", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=2000 },
         };
     }
 }

+ 0 - 27
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/SeedData/SysTenantMenuSeedData.cs

@@ -1,27 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Plugin.ApprovalFlow;
-
-/// <summary>
-/// 系统租户菜单表种子数据
-/// </summary>
-[IgnoreUpdateSeed]
-public class SysTenantMenuSeedData : ISqlSugarEntitySeedData<SysTenantMenu>
-{
-    /// <summary>
-    /// 种子数据
-    /// </summary>
-    /// <returns></returns>
-    public IEnumerable<SysTenantMenu> HasData()
-    {
-        return new SysMenuSeedData().HasData().Select(u => new SysTenantMenu
-        {
-            Id = CommonUtil.GetFixedHashCode("" + SqlSugarConst.DefaultTenantId + u.Id, 1300000000000),
-            TenantId = SqlSugarConst.DefaultTenantId, MenuId = u.Id
-        });
-    }
-}

+ 38 - 29
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/ApprovalFlowService.cs

@@ -4,14 +4,14 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
-using Microsoft.AspNetCore.Http;
+using System.Text.Json;
 
 namespace Admin.NET.Plugin.ApprovalFlow.Service;
 
 /// <summary>
 /// 审批流程服务
 /// </summary>
-[ApiDescriptionSettings(ApprovalFlowConst.GroupName, Order = 100)]
+[ApiDescriptionSettings(ApprovalFlowConst.GroupName, Order = 100, Description = "审批流程")]
 public class ApprovalFlowService : IDynamicApiController, ITransient
 {
     private readonly SqlSugarRepository<ApprovalFlow> _approvalFlowRep;
@@ -31,10 +31,10 @@ public class ApprovalFlowService : IDynamicApiController, ITransient
     public async Task<SqlSugarPagedList<ApprovalFlowOutput>> Page(ApprovalFlowInput input)
     {
         return await _approvalFlowRep.AsQueryable()
+            .WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.Code.Contains(input.Keyword.Trim()) || u.Name.Contains(input.Keyword.Trim()) || u.Remark.Contains(input.Keyword.Trim()))
             .WhereIF(!string.IsNullOrWhiteSpace(input.Code), u => u.Code.Contains(input.Code.Trim()))
             .WhereIF(!string.IsNullOrWhiteSpace(input.Name), u => u.Name.Contains(input.Name.Trim()))
             .WhereIF(!string.IsNullOrWhiteSpace(input.Remark), u => u.Remark.Contains(input.Remark.Trim()))
-            .WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.Code.Contains(input.Keyword.Trim()) || u.Name.Contains(input.Keyword.Trim()) || u.Remark.Contains(input.Keyword.Trim()))
             .Select<ApprovalFlowOutput>()
             .ToPagedListAsync(input.Page, input.PageSize);
     }
@@ -48,7 +48,10 @@ public class ApprovalFlowService : IDynamicApiController, ITransient
     public async Task<long> Add(AddApprovalFlowInput input)
     {
         var entity = input.Adapt<ApprovalFlow>();
-        if (input.Code == null) entity.Code = await LastCode("");
+        if (input.Code == null)
+        {
+            entity.Code = await LastCode("");
+        }
         await _approvalFlowRep.InsertAsync(entity);
         return entity.Id;
     }
@@ -71,9 +74,9 @@ public class ApprovalFlowService : IDynamicApiController, ITransient
     /// <param name="input"></param>
     /// <returns></returns>
     [ApiDescriptionSettings(Name = "Delete"), HttpPost]
-    public async Task Delete(BaseIdInput input)
+    public async Task Delete(DeleteApprovalFlowInput input)
     {
-        var entity = await _approvalFlowRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+        var entity = await _approvalFlowRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
         await _approvalFlowRep.FakeDeleteAsync(entity);  // 假删除
     }
 
@@ -82,9 +85,9 @@ public class ApprovalFlowService : IDynamicApiController, ITransient
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
-    public async Task<ApprovalFlow> GetDetail([FromQuery] BaseIdInput input)
+    public async Task<ApprovalFlow> GetDetail([FromQuery] QueryByIdApprovalFlowInput input)
     {
-        return await _approvalFlowRep.GetFirstAsync(u => u.Id == input.Id);
+        return await _approvalFlowRep.GetByIdAsync(input.Id);
     }
 
     /// <summary>
@@ -116,30 +119,36 @@ public class ApprovalFlowService : IDynamicApiController, ITransient
     {
         var today = DateTime.Now.Date;
         var count = await _approvalFlowRep.AsQueryable().Where(u => u.CreateTime >= today).CountAsync();
-        return prefix + DateTime.Now.ToString("yyMMdd") + $"{count + 1:d2}";
+        return prefix + DateTime.Now.ToString("yyMMdd") + string.Format("{0:d2}", count + 1);
     }
 
-    /// <summary>
-    /// 匹配审批流程
-    /// </summary>
-    /// <param name="context"></param>
-    /// <returns></returns>
-    [NonAction]
-    public async Task MatchApproval(HttpContext context)
+    [HttpGet]
+    [ApiDescriptionSettings(Name = "FlowList")]
+    [DisplayName("获取审批流结构")]
+    public async Task<dynamic> FlowList([FromQuery] string code)
     {
-        var request = context.Request;
-        var response = context.Response;
-
-        var path = request.Path.ToString().Split("/");
-
-        var method = request.Method;
-        var query = request.QueryString;
-        var header = request.Headers;
-        var body = request.Body;
-
-        var requestHeaders = request.Headers;
-        var responseHeaders = response.Headers;
+        var result = await _approvalFlowRep.AsQueryable().Where(u => u.Code == code).Select<ApprovalFlowOutput>().FirstAsync();
+        var FlowJson = result.FlowJson != null ? JsonSerializer.Deserialize<ApprovalFlowItem>(result.FlowJson) : new ApprovalFlowItem();
+        var FormJson = result.FormJson != null ? JsonSerializer.Deserialize<ApprovalFormItem>(result.FormJson) : new ApprovalFormItem();
+        return new
+        {
+            FlowJson,
+            FormJson
+        };
+    }
 
-        await Task.CompletedTask;
+    [HttpGet]
+    [ApiDescriptionSettings(Name = "FormRoutes")]
+    [DisplayName("获取审批流规则")]
+    public async Task<List<string>> FormRoutes()
+    {
+        var results = await _approvalFlowRep.AsQueryable().Select<ApprovalFlowOutput>().ToListAsync();
+        var list = new List<string>();
+        foreach (var item in results)
+        {
+            var FormJson = item.FormJson != null ? JsonSerializer.Deserialize<ApprovalFormItem>(item.FormJson) : new ApprovalFormItem();
+            if (item.FormJson != null) list.Add(FormJson.Route);
+        }
+        return list;
     }
 }

+ 95 - 11
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/Dto/ApprovalFlowInput.cs

@@ -6,6 +6,82 @@
 
 namespace Admin.NET.Plugin.ApprovalFlow.Service;
 
+/// <summary>
+/// 审批流基础输入参数
+/// </summary>
+public class ApprovalFlowBaseInput
+{
+    /// <summary>
+    /// 编号
+    /// </summary>
+    public virtual string? Code { get; set; }
+
+    /// <summary>
+    /// 名称
+    /// </summary>
+    public virtual string? Name { get; set; }
+
+    /// <summary>
+    /// 表单
+    /// </summary>
+    public virtual string? FormJson { get; set; }
+
+    /// <summary>
+    /// 流程
+    /// </summary>
+    public virtual string? FlowJson { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public virtual string? Remark { get; set; }
+
+    /// <summary>
+    /// 创建时间
+    /// </summary>
+    public virtual DateTime? CreateTime { get; set; }
+
+    /// <summary>
+    /// 更新时间
+    /// </summary>
+    public virtual DateTime? UpdateTime { get; set; }
+
+    /// <summary>
+    /// 创建者Id
+    /// </summary>
+    public virtual long? CreateUserId { get; set; }
+
+    /// <summary>
+    /// 创建者姓名
+    /// </summary>
+    public virtual string? CreateUserName { get; set; }
+
+    /// <summary>
+    /// 修改者Id
+    /// </summary>
+    public virtual long? UpdateUserId { get; set; }
+
+    /// <summary>
+    /// 修改者姓名
+    /// </summary>
+    public virtual string? UpdateUserName { get; set; }
+
+    /// <summary>
+    /// 创建者部门Id
+    /// </summary>
+    public virtual long? CreateOrgId { get; set; }
+
+    /// <summary>
+    /// 创建者部门名称
+    /// </summary>
+    public virtual string? CreateOrgName { get; set; }
+
+    /// <summary>
+    /// 软删除
+    /// </summary>
+    public virtual bool IsDelete { get; set; }
+}
+
 /// <summary>
 /// 审批流分页查询输入参数
 /// </summary>
@@ -30,29 +106,37 @@ public class ApprovalFlowInput : BasePageInput
 /// <summary>
 /// 审批流增加输入参数
 /// </summary>
-public class AddApprovalFlowInput : ApprovalFlow
+public class AddApprovalFlowInput : ApprovalFlowBaseInput
 {
     /// <summary>
-    /// 名称
+    /// 软删除
     /// </summary>
-    [Required(ErrorMessage = "名称不能为空")]
-    public override string Name { get; set; }
+    [Required(ErrorMessage = "软删除不能为空")]
+    public override bool IsDelete { get; set; }
+}
 
-    /// <summary>
-    /// 状态
-    /// </summary>
-    [Required(ErrorMessage = "状态不能为空")]
-    public override int? Status { get; set; }
+/// <summary>
+/// 审批流删除输入参数
+/// </summary>
+public class DeleteApprovalFlowInput : BaseIdInput
+{
 }
 
 /// <summary>
 /// 审批流更新输入参数
 /// </summary>
-public class UpdateApprovalFlowInput : ApprovalFlow
+public class UpdateApprovalFlowInput : ApprovalFlowBaseInput
 {
     /// <summary>
     /// 主键Id
     /// </summary>
     [Required(ErrorMessage = "主键Id不能为空")]
-    public override long Id { get; set; }
+    public long Id { get; set; }
+}
+
+/// <summary>
+/// 审批流主键查询输入参数
+/// </summary>
+public class QueryByIdApprovalFlowInput : DeleteApprovalFlowInput
+{
 }

+ 96 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/Dto/ApprovalFlowItem.cs

@@ -0,0 +1,96 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using System.Text.Json.Serialization;
+
+namespace Admin.NET.Plugin.ApprovalFlow.Service;
+
+public class ApprovalFlowItem
+{
+    [JsonPropertyName("nodes")]
+    public List<ApprovalFlowNodeItem> Nodes { get; set; }
+
+    [JsonPropertyName("edges")]
+    public List<ApprovalFlowEdgeItem> Edges { get; set; }
+}
+
+public class ApprovalFlowNodeItem
+{
+    [JsonPropertyName("id")]
+    public string Id { get; set; }
+
+    [JsonPropertyName("type")]
+    public string Type { get; set; }
+
+    [JsonPropertyName("x")]
+    public float X { get; set; }
+
+    [JsonPropertyName("y")]
+    public float Y { get; set; }
+
+    [JsonPropertyName("properties")]
+    public FlowProperties Properties { get; set; }
+
+    [JsonPropertyName("text")]
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+    public FlowTextItem Text { get; set; }
+}
+
+public class ApprovalFlowEdgeItem
+{
+    [JsonPropertyName("id")]
+    public string Id { get; set; }
+
+    [JsonPropertyName("type")]
+    public string Type { get; set; }
+
+    [JsonPropertyName("sourceNodeId")]
+    public string SourceNodeId { get; set; }
+
+    [JsonPropertyName("targetNodeId")]
+    public string TargetNodeId { get; set; }
+
+    [JsonPropertyName("startPoint")]
+    public FlowEdgePointItem StartPoint { get; set; }
+
+    [JsonPropertyName("endPoint")]
+    public FlowEdgePointItem EndPoint { get; set; }
+
+    [JsonPropertyName("properties")]
+    public FlowProperties Properties { get; set; }
+
+    [JsonPropertyName("text")]
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+    public FlowTextItem Text { get; set; }
+
+    [JsonPropertyName("pointsList")]
+    public List<FlowEdgePointItem> PointsList { get; set; }
+}
+
+public class FlowProperties
+{
+}
+
+public class FlowTextItem
+{
+    [JsonPropertyName("x")]
+    public float X { get; set; }
+
+    [JsonPropertyName("y")]
+    public float Y { get; set; }
+
+    [JsonPropertyName("value")]
+    public string Value { get; set; }
+}
+
+public class FlowEdgePointItem
+{
+    [JsonPropertyName("x")]
+    public float X { get; set; }
+
+    [JsonPropertyName("y")]
+    public float Y { get; set; }
+}

+ 27 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/ApprovalFlow/Dto/ApprovalFormItem.cs

@@ -0,0 +1,27 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using System.Text.Json.Serialization;
+
+namespace Admin.NET.Plugin.ApprovalFlow.Service;
+
+public class ApprovalFormItem
+{
+    [JsonPropertyName("configId")]
+    public string ConfigId { get; set; }
+
+    [JsonPropertyName("tableName")]
+    public string TableName { get; set; }
+
+    [JsonPropertyName("entityName")]
+    public string EntityName { get; set; }
+
+    [JsonPropertyName("typeName")]
+    public string TypeName { get; set; }
+
+    [JsonPropertyName("route")]
+    public string Route => EntityName[..1].ToLower() + EntityName[1..] + "/" + TypeName;
+}

+ 81 - 0
Admin.NET/Plugins/Admin.NET.Plugin.ApprovalFlow/Service/SysApproval/SysApprovalService.cs

@@ -0,0 +1,81 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Microsoft.AspNetCore.Http;
+
+namespace Admin.NET.Plugin.ApprovalFlow.Service;
+
+public class SysApprovalService : ITransient
+{
+    private readonly SqlSugarRepository<ApprovalFlowRecord> _approvalFlowRep;
+    private readonly SqlSugarRepository<ApprovalFormRecord> _approvalFormRep;
+    private readonly ApprovalFlowService _approvalFlowService;
+
+    public SysApprovalService(SqlSugarRepository<ApprovalFlowRecord> approvalFlowRep, SqlSugarRepository<ApprovalFormRecord> approvalFormRep, ApprovalFlowService approvalFlowService)
+    {
+        _approvalFlowRep = approvalFlowRep;
+        _approvalFormRep = approvalFormRep;
+        _approvalFlowService = approvalFlowService;
+    }
+
+    /// <summary>
+    /// 匹配审批流程
+    /// </summary>
+    /// <param name="context"></param>
+    /// <returns></returns>
+    [NonAction]
+    public async Task MatchApproval(HttpContext context)
+    {
+        var request = context.Request;
+        var response = context.Response;
+
+        var path = request.Path.ToString().Split("/");
+
+        var method = request.Method;
+        var qs = request.QueryString;
+        var h = request.Headers;
+        var b = request.Body;
+
+        var requestHeaders = request.Headers;
+        var responseHeaders = response.Headers;
+
+        var serviceName = path[1];
+        if (serviceName.StartsWith("api"))
+        {
+            if (path.Length > 3)
+            {
+                var funcName = path[2];
+                var typeName = path[3];
+
+                var list = await _approvalFlowService.FormRoutes();
+                if (list.Any(u => u.Contains(funcName) && u.Contains(typeName)))
+                {
+                    var approvalFlow = new ApprovalFlowRecord
+                    {
+                        FormName = funcName,
+                        CreateTime = DateTime.Now,
+                    };
+
+                    // 判断是否需要审批
+                    await _approvalFlowRep.InsertAsync(approvalFlow);
+
+                    var approvalForm = new ApprovalFormRecord
+                    {
+                        FlowId = approvalFlow.Id,
+                        FormName = funcName,
+                        FormType = typeName,
+                        CreateTime = DateTime.Now,
+                    };
+
+                    // 判断是否需要审批
+                    await _approvalFormRep.InsertAsync(approvalForm);
+                }
+            }
+        }
+
+        await Task.CompletedTask;
+    }
+}

+ 1 - 4
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Admin.NET.Plugin.DingTalk.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
     <NoWarn>1701;1702;1591;8632</NoWarn>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>disable</Nullable>
@@ -19,8 +19,5 @@
   <ItemGroup>
     <ProjectReference Include="..\..\Admin.NET.Core\Admin.NET.Core.csproj" />
   </ItemGroup>
-	<ItemGroup>
-		<Folder Include="..\RequestProxy" />
-	</ItemGroup>
 
 </Project>

+ 3 - 5
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Configuration/DingTalk.json

@@ -9,11 +9,9 @@
     "Order": 90
   },
   "DingTalk": {
-    "AppId": "xxxx",
-    "AgentId": 123,
+    "AppId": "",
+    "AgentId": "",
     "ClientId": "xxxx", // 原 AppKey 和 SuiteKey
-    "ClientSecret": "xxxx", // 原 AppSecret 和 SuiteSecret
-    "CorpId": "xxxx",
-    "TenantId": 1300000001001
+    "ClientSecret": "xxxx" // 原 AppSecret 和 SuiteSecret
   }
 }

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

@@ -17,11 +17,6 @@ public class DingTalkConst
     /// </summary>
     public const string GroupName = "DingTalk";
 
-    /// <summary>
-    /// AccessToken的缓存键
-    /// </summary>
-    public const string AccessTokenKeyPrefix = "DingTalkAccessToken:";
-
     /// <summary>
     /// 姓名
     /// </summary>
@@ -40,20 +35,15 @@ public class DingTalkConst
     /// <summary>
     /// 主部门Id
     /// </summary>
-    public const string DeptIdField = "sys00-mainDeptId";
+    public const string DeptId = "sys00-mainDeptId";
 
     /// <summary>
     /// 主部门
     /// </summary>
-    public const string DeptField = "sys00-mainDept";
+    public const string Dept = "sys00-mainDept";
 
     /// <summary>
     /// 职位
     /// </summary>
-    public const string PositionField = "sys00-position";
-
-    /// <summary>
-    /// 邮箱
-    /// </summary>
-    public const string EmailField = "sys00-email";
+    public const string Position = "sys00-position";
 }

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

@@ -10,7 +10,7 @@ namespace Admin.NET.Plugin.DingTalk;
 /// 钉钉用户表
 /// </summary>
 [SugarTable(null, "钉钉用户表")]
-public class DingTalkUser : EntityTenant
+public class DingTalkUser : EntityBase
 {
     /// <summary>
     /// 系统用户Id
@@ -31,7 +31,7 @@ public class DingTalkUser : EntityTenant
     /// </summary>
     [SugarColumn(ColumnDescription = "钉钉用户id", Length = 64)]
     [Required, MaxLength(64)]
-    public virtual string DingTalkUserId { get; set; }
+    public virtual string? DingTalkUserId { get; set; }
 
     /// <summary>
     /// UnionId

+ 1 - 2
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/GlobalUsings.cs

@@ -10,8 +10,7 @@ global using Furion.ConfigurableOptions;
 global using Furion.DependencyInjection;
 global using Furion.DynamicApiController;
 global using Furion.FriendlyException;
-global using Furion.RemoteRequest;
-global using Microsoft.AspNetCore.Http;
+global using Furion.HttpRemote;
 global using Microsoft.AspNetCore.Mvc;
 global using Microsoft.Extensions.Options;
 global using Newtonsoft.Json;

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

@@ -5,12 +5,7 @@
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 using Admin.NET.Plugin.DingTalk;
-using Admin.NET.Plugin.DingTalk.RequestProxy.HRM;
-using Admin.NET.Plugin.DingTalk.RequestProxy.HRM.DTO;
-using Admin.NET.Plugin.DingTalk.Service;
-
 using Furion.Schedule;
-
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 
@@ -24,44 +19,51 @@ namespace Admin.NET.Plugin.Job;
 public class SyncDingTalkUserJob : IJob
 {
     private readonly IServiceScopeFactory _scopeFactory;
+    private readonly IDingTalkApi _dingTalkApi;
     private readonly ILogger _logger;
 
-    public SyncDingTalkUserJob(IServiceScopeFactory scopeFactory, ILoggerFactory loggerFactory)
+    public SyncDingTalkUserJob(IServiceScopeFactory scopeFactory, IDingTalkApi dingTalkApi, ILoggerFactory loggerFactory)
     {
         _scopeFactory = scopeFactory;
+        _dingTalkApi = dingTalkApi;
         _logger = loggerFactory.CreateLogger(CommonConst.SysLogCategoryName);
     }
 
     public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
     {
         using var serviceScope = _scopeFactory.CreateScope();
-        var sysUserRepo = serviceScope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysUser>>();
-        var dingTalkUserRepo = serviceScope.ServiceProvider.GetRequiredService<SqlSugarRepository<DingTalkUser>>();
-        var dingTalkOptions = serviceScope.ServiceProvider.GetRequiredService<IOptions<DingTalkOptions>>().Value;
-        var dingTalkService = serviceScope.ServiceProvider.GetRequiredService<DingTalkService>();
-        var hrmRequest = serviceScope.ServiceProvider.GetRequiredService<HrmRequest>();
+        var _sysUserRep = serviceScope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysUser>>();
+        var _dingTalkUserRepo = serviceScope.ServiceProvider.GetRequiredService<SqlSugarRepository<DingTalkUser>>();
+        var _dingTalkOptions = serviceScope.ServiceProvider.GetRequiredService<IOptions<DingTalkOptions>>();
 
         // 获取Token
-        var token = await dingTalkService.GetDingTalkToken();
-        ArgumentNullException.ThrowIfNull(token);
+        var tokenRes = await _dingTalkApi.GetDingTalkToken(_dingTalkOptions.Value.ClientId, _dingTalkOptions.Value.ClientSecret);
+        if (tokenRes.ErrCode != 0)
+            throw Oops.Oh(tokenRes.ErrMsg);
 
-        var dingTalkUserList = new List<RosterListResultDomain>();
+        var dingTalkUserList = new List<DingTalkEmpRosterFieldVo>();
         var offset = 0;
         while (offset >= 0)
         {
             // 获取用户Id列表
-            var userIdsRes = await hrmRequest.EmployeeQueryOnJob(token,
-                new List<string> { "2", "3", "5", "-1" }, 50, offset);
+            var userIdsRes = await _dingTalkApi.GetDingTalkCurrentEmployeesList(tokenRes.AccessToken, new GetDingTalkCurrentEmployeesListInput
+            {
+                StatusList = "2,3,5,-1",
+                Size = 50,
+                Offset = offset
+            });
             if (!userIdsRes.Success)
             {
                 _logger.LogError(userIdsRes.ErrMsg);
                 break;
             }
             // 根据用户Id获取花名册
-            var rosterRes = await hrmRequest.RosterListsQuery(token,
-                userIdsRes.Result.DataList,
-                new List<string> { DingTalkConst.NameField, DingTalkConst.JobNumberField, DingTalkConst.MobileField, DingTalkConst.DeptField, DingTalkConst.DeptIdField, DingTalkConst.PositionField },
-                dingTalkOptions.AgentId);
+            var rosterRes = await _dingTalkApi.GetDingTalkCurrentEmployeesRosterList(tokenRes.AccessToken, new GetDingTalkCurrentEmployeesRosterListInput()
+            {
+                UserIdList = string.Join(",", userIdsRes.Result.DataList),
+                FieldFilterList = $"{DingTalkConst.NameField},{DingTalkConst.JobNumberField},{DingTalkConst.MobileField}",
+                AgentId = _dingTalkOptions.Value.AgentId
+            });
             if (!rosterRes.Success)
             {
                 _logger.LogError(rosterRes.ErrMsg);
@@ -77,14 +79,14 @@ public class SyncDingTalkUserJob : IJob
         }
 
         // 判断新增还是更新
-        var sysDingTalkUserIdList = await dingTalkUserRepo.AsQueryable().Select(u => new
+        var sysDingTalkUserIdList = await _dingTalkUserRepo.AsQueryable().Select(u => new
         {
             u.Id,
             u.DingTalkUserId
         }).ToListAsync();
 
         var uDingTalkUser = dingTalkUserList.Where(u => sysDingTalkUserIdList.Any(m => m.DingTalkUserId == u.UserId)); // 需要更新的用户Id
-        var iDingTalkUser = dingTalkUserList.Where(u => sysDingTalkUserIdList.All(m => m.DingTalkUserId != u.UserId)); // 需要新增的用户Id
+        var iDingTalkUser = dingTalkUserList.Where(u => !sysDingTalkUserIdList.Any(m => m.DingTalkUserId == u.UserId)); // 需要新增的用户Id
 
         // 新增钉钉用户
         var iUser = iDingTalkUser.Select(res => new DingTalkUser
@@ -93,14 +95,13 @@ 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.DeptIdField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
-            Dept = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.DeptField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
-            Position = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.PositionField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
-            TenantId = dingTalkOptions.TenantId
+            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)
         {
-            await dingTalkUserRepo.CopyNew().AsInsertable(iUser).ExecuteCommandAsync();
+            await _dingTalkUserRepo.CopyNew().AsInsertable(iUser).ExecuteCommandAsync();
         }
 
         // 更新钉钉用户
@@ -111,14 +112,13 @@ 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.DeptIdField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
-            Dept = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.DeptField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
-            Position = res.FieldDataList.Where(u => u.FieldCode == DingTalkConst.PositionField).Select(u => u.FieldValueList.Select(m => m.Value).FirstOrDefault()).FirstOrDefault(),
-            TenantId = dingTalkOptions.TenantId,
+            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)
         {
-            await dingTalkUserRepo.CopyNew().AsUpdateable(uUser).UpdateColumns(u => new
+            await _dingTalkUserRepo.CopyNew().AsUpdateable(uUser).UpdateColumns(u => new
             {
                 u.DingTalkUserId,
                 u.Name,
@@ -130,40 +130,40 @@ public class SyncDingTalkUserJob : IJob
                 u.UpdateTime,
                 u.UpdateUserName,
                 u.UpdateUserId,
-                u.TenantId
             }).ExecuteCommandAsync();
         }
 
-        //获取没有对应SysUser的DingTalkUser
-        var dingTalkWithoutSysuser = await dingTalkUserRepo.AsQueryable()
-           .LeftJoin<SysUser>((dUser, sUser) => dUser.DingTalkUserId == sUser.Account)
-           .GroupBy((dUser, sUser) => dUser.Id)
-           .Having((dUser, sUser) => SqlFunc.AggregateCount(sUser.Id) == 0)
-           .Select((dUser, sUser) => dUser)
-           .ToListAsync();
-
-        //插入对应的SysUser
-        if (dingTalkWithoutSysuser.Count > 0)
-        {
-            var iSysUser = dingTalkWithoutSysuser.Select(dUser => new SysUser
+        // 通过系统用户账号(工号),更新钉钉用户表里面的系统用户Id
+        var sysUser = await _sysUserRep.AsQueryable()
+            .Select(u => new
             {
-                Account = dUser.DingTalkUserId,
-                Password = CryptogramUtil.Encrypt(dUser.DingTalkUserId),
-                RealName = dUser.Name ?? "",
-                TenantId = dingTalkOptions.TenantId,
-            }).ToList();
-            await sysUserRepo.InsertRangeAsync(iSysUser);
-        }
-
-        //将SysUser.Id回填到对应DingTalkUser.SysUserId
-        await dingTalkUserRepo.AsUpdateable()
-            .SetColumns(dUser => new DingTalkUser
+                u.Id,
+                u.Account
+            }).ToListAsync();
+        var sysDingTalkUser = await _dingTalkUserRepo.AsQueryable()
+            .Where(u => sysUser.Any(m => m.Account == u.JobNumber))
+            .Select(u => new
             {
-                SysUserId = SqlFunc.Subqueryable<SysUser>()
-                                    .Where(sUser => sUser.Account == dUser.DingTalkUserId)
-                                    .Select(sUser => sUser.Id)
-            })
-            .ExecuteCommandAsync();
+                u.Id,
+                u.JobNumber,
+                u.Mobile,
+                u.DeptId,
+                u.Dept,
+                u.Position
+            }).ToListAsync();
+        var uSysDingTalkUser = sysDingTalkUser.Select(u => new DingTalkUser
+        {
+            Id = u.Id,
+            SysUserId = sysUser.Where(m => m.Account == u.JobNumber).Select(u => u.Id).FirstOrDefault(),
+        }).ToList();
+
+        await _dingTalkUserRepo.CopyNew().AsUpdateable(uSysDingTalkUser).UpdateColumns(u => new
+        {
+            u.SysUserId,
+            u.UpdateTime,
+            u.UpdateUserName,
+            u.UpdateUserId,
+        }).ExecuteCommandAsync();
 
         var originColor = Console.ForegroundColor;
         Console.ForegroundColor = ConsoleColor.Blue;

+ 1 - 11
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Option/DingTalkOptions.cs

@@ -16,7 +16,7 @@ public sealed class DingTalkOptions : IConfigurableOptions
     /// <summary>
     /// AgentId
     /// </summary>
-    public long AgentId { get; set; }
+    public string AgentId { get; set; }
 
     /// <summary>
     /// 原 AppKey 和 SuiteKey
@@ -27,14 +27,4 @@ public sealed class DingTalkOptions : IConfigurableOptions
     /// 原 AppSecret 和 SuiteSecret
     /// </summary>
     public string ClientSecret { get; set; }
-
-    /// <summary>
-    /// 所属企业的CorpId
-    /// </summary>
-    public string CorpId { get; set; }
-
-    /// <summary>
-    /// 租户Id
-    /// </summary>
-    public long TenantId { get; set; }
 }

+ 0 - 58
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/AliTripRequest.cs

@@ -1,58 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Admin.NET.Plugin.DingTalk.RequestProxy.AliTrip.DTO;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.AliTrip;
-
-public class AliTripRequest : IScoped
-{
-    private readonly IAliTripRequestProxy _request;
-
-    public AliTripRequest(IAliTripRequestProxy request)
-    {
-        _request = request;
-    }
-
-    /// <summary>
-    /// 获取阿里商旅访问地址
-    /// </summary>
-    /// <param name="accessToken"></param>
-    /// <param name="req"></param>
-    /// <returns></returns>
-    public async Task<GetAliTripAddressResponse> GetAliTripAddress(string accessToken, AliTripAddressRequestDomain req)
-    {
-        var resStr = await _request.GetAliTripAddress(accessToken, new GetAliTripAddressRequest
-        {
-            Request = req
-        });
-        return resStr.ToObject<GetAliTripAddressResponse>();
-    }
-
-    /// <summary>
-    /// 获取企业机票订单数据
-    /// </summary>
-    /// <param name="accessToken"></param>
-    /// <param name="rq"></param>
-    /// <returns></returns>
-    public async Task<GetAliTripFlightOrdersResponse> GetAliTripFlightOrders(string accessToken, GetAliTripFlightOrdersRequest rq)
-    {
-        var resStr = await _request.GetAliTripFlightOrders(accessToken, rq);
-        return resStr.ToObject<GetAliTripFlightOrdersResponse>();
-    }
-
-    /// <summary>
-    /// 获取企业火车票订单数据
-    /// </summary>
-    /// <param name="accessToken"></param>
-    /// <param name="rq"></param>
-    /// <returns></returns>
-    public async Task<GetAliTripTrainOrdersResponse> GetAliTripTrainOrders(string accessToken, GetAliTripTrainOrdersRequest rq)
-    {
-        var resStr = await _request.GetAliTripTrainOrders(accessToken, rq);
-        return resStr.ToObject<GetAliTripTrainOrdersResponse>();
-    }
-}

+ 0 - 70
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/Enums.cs

@@ -1,70 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.AliTrip.DTO;
-
-/// <summary>
-/// 阿里商旅结算方式
-/// </summary>
-[SuppressSniffer]
-public enum AliTripPricePayTypeEnum
-{
-    个人现付 = 1,
-    企业现付 = 2,
-    企业月结 = 4,
-    企业预存 = 8
-}
-
-/// <summary>
-/// 阿里商旅资金流向
-/// </summary>
-[SuppressSniffer]
-public enum AliTripPriceTypeEnum
-{
-    支出 = 1,
-    收入 = 2
-}
-
-/// <summary>
-/// 状态
-/// </summary>
-[SuppressSniffer]
-public enum AliTripFlightOrdersInsureInfoStatusEnum
-{
-    已出保 = 1,
-    已退保 = 2
-}
-
-/// <summary>
-/// 行程类型
-/// </summary>
-[SuppressSniffer]
-public enum AliTripFlightOrdersTripTypeEnum
-{
-    单程 = 0,
-    往返 = 1,
-    中转 = 2
-}
-
-/// <summary>
-/// 订单状态
-/// </summary>
-[SuppressSniffer]
-public enum AliTripTrainOrdersStatusEnum
-{
-    待支付 = 0,
-    出票中 = 1,
-    已关闭 = 2,
-    改签成功 = 3,
-    退票成功 = 4,
-    出票完成 = 5,
-    退票申请中 = 6,
-    改签申请中 = 7,
-    已出票or已发货 = 8,
-    出票失败 = 9,
-    改签失败 = 10,
-    退票失败 = 11
-}

+ 0 - 85
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/GetAlitripAddressRequest.cs

@@ -1,85 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.AliTrip.DTO;
-
-/// <summary>
-/// 获取阿里商旅访问地址
-/// </summary>
-public class GetAliTripAddressRequest
-{
-    [JsonProperty("request")]
-    [JsonPropertyName("request")]
-    public AliTripAddressRequestDomain Request { get; set; }
-}
-
-public class AliTripAddressRequestDomain
-{
-    /// <summary>
-    /// 用户id
-    /// </summary>
-    [JsonProperty("userid")]
-    [JsonPropertyName("userid")]
-    public string? UserId { get; set; }
-
-    [JsonProperty("corpid")]
-    [JsonPropertyName("corpid")]
-    public string CorpId { get; set; }
-
-    /// <summary>
-    /// 类目类型:1:机票,2:火车票,3:酒店,4:用车
-    /// </summary>
-    [JsonProperty("type")]
-    [JsonPropertyName("type")]
-    public AliTripTypeEnum Type { get; set; }
-
-    /// <summary>
-    /// 类目类型
-    /// </summary>
-    [SuppressSniffer]
-    public enum AliTripTypeEnum
-    {
-        机票 = 1,
-        火车票 = 2,
-        酒店 = 3,
-        用车 = 4
-    }
-
-    /// <summary>
-    /// 操作类型:1:预订,2:我的订单列表,3:商旅管理后台,如果需要获取该场景的地址,只需提供corpid,userid,4:商旅h5主页
-    /// </summary>
-    [JsonProperty("action_type")]
-    [JsonPropertyName("action_type")]
-    public AliTripActionTypeEnum ActionType { get; set; }
-
-    /// <summary>
-    /// 操作类型
-    /// </summary>
-    [SuppressSniffer]
-    public enum AliTripActionTypeEnum
-    {
-        预订 = 1,
-        我的订单列表 = 2,
-        商旅管理后台 = 3,
-        商旅h5主页 = 4
-    }
-
-    /// <summary>
-    /// 第三方行程ID。存在代表通过审批单预订,不存在代表特殊场景:普通员工是预览,特殊授权人和代订人是免审批预订场景。
-    /// </summary>
-    [JsonProperty("itinerary_id")]
-    [JsonPropertyName("itinerary_id")]
-    public string ItineraryId { get; set; }
-
-    /// <summary>
-    /// 员工第一次使用用车需要手机号,与司机联系。
-    /// </summary>
-    [JsonProperty("phone")]
-    [JsonPropertyName("phone")]
-    public string Phone { get; set; }
-}

+ 0 - 25
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/GetAlitripAddressResponse.cs

@@ -1,25 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Admin.NET.Plugin.DingTalk.RequestProxy.BaseTypes;
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.AliTrip.DTO;
-
-/// <summary>
-/// 获取阿里商旅访问地址
-/// </summary>
-public class GetAliTripAddressResponse : DingtalkResponseErrorResult<AliTripAddressResponseResultDomain>
-{
-}
-
-public class AliTripAddressResponseResultDomain
-{
-    [JsonProperty("url")]
-    [JsonPropertyName("url")]
-    public string Url { get; set; }
-}

+ 0 - 104
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/GetAlitripFlightOrdersRequest.cs

@@ -1,104 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.AliTrip.DTO;
-
-/// <summary>
-/// 获取企业机票订单数据
-/// </summary>
-public class GetAliTripFlightOrdersRequest
-{
-    public GetAliTripFlightOrdersRequestDomain rq;
-}
-
-public class GetAliTripFlightOrdersRequestDomain
-{
-    /// <summary>
-    /// 开始时间 2017-05-01 00:00:00
-    /// </summary>
-    [JsonProperty("start_time")]
-    [JsonPropertyName("start_time")]
-    public string? StartTime { get; set; }
-
-    /// <summary>
-    /// 商旅申请单id
-    /// </summary>
-    [JsonProperty("apply_id")]
-    [JsonPropertyName("apply_id")]
-    public string? ApplyId { get; set; }
-
-    /// <summary>
-    /// 页数,从1开始
-    /// </summary>
-    [JsonProperty("page")]
-    [JsonPropertyName("page")]
-    public int? Page { get; set; }
-
-    /// <summary>
-    /// 用户id
-    /// </summary>
-    [JsonProperty("userid")]
-    [JsonPropertyName("userid")]
-    public string? UserId { get; set; }
-
-    /// <summary>
-    /// 每页数据量,默认10,最高50
-    /// </summary>
-    [JsonProperty("page_size")]
-    [JsonPropertyName("page_size")]
-    public int? PageSize { get; set; }
-
-    /// <summary>
-    /// 部门id
-    /// </summary>
-    [JsonProperty("deptid")]
-    [JsonPropertyName("deptid")]
-    public string? Deptid { get; set; }
-
-    /// <summary>
-    /// 结束时间 2017-05-01 00:00:00
-    /// </summary>
-    [JsonProperty("end_time")]
-    [JsonPropertyName("end_time")]
-    public string? EndTime { get; set; }
-
-    /// <summary>
-    /// 企业id
-    /// </summary>
-    [JsonProperty("corpid")]
-    [JsonPropertyName("corpid")]
-    public string? Corpid { get; set; }
-
-    /// <summary>
-    /// 更新结束时间 2017-05-01 00:00:00
-    /// </summary>
-    [JsonProperty("update_end_time")]
-    [JsonPropertyName("update_end_time")]
-    public string? UpdateEndTime { get; set; }
-
-    /// <summary>
-    /// 更新开始时间 2017-05-01 00:00:00
-    /// </summary>
-    [JsonProperty("update_start_time")]
-    [JsonPropertyName("update_start_time")]
-    public string? UpdateStartTime { get; set; }
-
-    /// <summary>
-    /// false:仅搜索未报销的申请单
-    /// </summary>
-    [JsonProperty("all_apply")]
-    [JsonPropertyName("all_apply")]
-    public string? AllApply { get; set; }
-
-    /// <summary>
-    /// 第三方申请单ID
-    /// </summary>
-    [JsonProperty("thirdpart_apply_id")]
-    [JsonPropertyName("thirdpart_apply_id")]
-    public string? ThirdpartApplyId { get; set; }
-}

+ 0 - 511
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/GetAlitripFlightOrdersResponse.cs

@@ -1,511 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Admin.NET.Plugin.DingTalk.RequestProxy.BaseTypes;
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.AliTrip.DTO;
-
-/// <summary>
-/// 获取企业机票订单数据
-/// </summary>
-public class GetAliTripFlightOrdersResponse : DingtalkResponseErrorSuccess
-{
-    /// <summary>
-    /// 机票列表
-    /// </summary>
-    [JsonProperty("flight_order_list")]
-    [JsonPropertyName("flight_order_list")]
-    public List<FlightOrderDomain> FlightOrderList { get; set; }
-
-    /// <summary>
-    /// 分页相关信息
-    /// </summary>
-    [JsonProperty("page_info")]
-    [JsonPropertyName("page_info")]
-    public PageInfoDomain PageInfo { get; set; }
-}
-
-public class FlightOrderDomain
-{
-    /// <summary>
-    /// 机票订单id
-    /// </summary>
-    [JsonProperty("id")]
-    [JsonPropertyName("id")]
-    public long Id { get; set; }
-
-    /// <summary>
-    /// 更新时间
-    /// </summary>
-    [JsonProperty("gmt_modified")]
-    [JsonPropertyName("gmt_modified")]
-    public DateTime GmtModified { get; set; }
-
-    /// <summary>
-    /// 用户id
-    /// </summary>
-    [JsonProperty("userid")]
-    [JsonPropertyName("userid")]
-    public string UserId { get; set; }
-
-    /// <summary>
-    /// 企业名称
-    /// </summary>
-    [JsonProperty("corp_name")]
-    [JsonPropertyName("corp_name")]
-    public string CorpName { get; set; }
-
-    /// <summary>
-    /// 企业id
-    /// </summary>
-    [JsonProperty("corpid")]
-    [JsonPropertyName("corpid")]
-    public string CorpId { get; set; }
-
-    /// <summary>
-    /// 创建时间
-    /// </summary>
-    [JsonProperty("gmt_create")]
-    [JsonPropertyName("gmt_create")]
-    public DateTime GmtCreate { get; set; }
-
-    /// <summary>
-    /// 用户名称
-    /// </summary>
-    [JsonProperty("user_name")]
-    [JsonPropertyName("user_name")]
-    public string UserName { get; set; }
-
-    /// <summary>
-    /// 部门id
-    /// </summary>
-    [JsonProperty("deptid")]
-    [JsonPropertyName("deptid")]
-    public string DeptId { get; set; }
-
-    /// <summary>
-    /// 部门名称
-    /// </summary>
-    [JsonProperty("dept_name")]
-    [JsonPropertyName("dept_name")]
-    public string DeptName { get; set; }
-
-    /// <summary>
-    /// 商旅申请单id
-    /// </summary>
-    [JsonProperty("apply_id")]
-    [JsonPropertyName("apply_id")]
-    public string ApplyId { get; set; }
-
-    /// <summary>
-    /// 联系人
-    /// </summary>
-    [JsonProperty("contact_name")]
-    [JsonPropertyName("contact_name")]
-    public string ContactName { get; set; }
-
-    /// <summary>
-    /// 出发城市
-    /// </summary>
-    [JsonProperty("dep_city")]
-    [JsonPropertyName("dep_city")]
-    public string DepCity { get; set; }
-
-    /// <summary>
-    /// 到达城市
-    /// </summary>
-    [JsonProperty("arr_city")]
-    [JsonPropertyName("arr_city")]
-    public string ArrCity { get; set; }
-
-    /// <summary>
-    /// 出发日期
-    /// </summary>
-    [JsonProperty("dep_date")]
-    [JsonPropertyName("dep_date")]
-    public DateTime DepDate { get; set; }
-
-    /// <summary>
-    /// 到达日期
-    /// </summary>
-    [JsonProperty("ret_date")]
-    [JsonPropertyName("ret_date")]
-    public DateTime RetDate { get; set; }
-
-    /// <summary>
-    /// 行程类型。0:单程,1:往返,2:中转
-    /// </summary>
-    [JsonProperty("trip_type")]
-    [JsonPropertyName("trip_type")]
-    public AliTripFlightOrdersTripTypeEnum TripType { get; set; }
-
-    /// <summary>
-    /// 乘机人数量
-    /// </summary>
-    [JsonProperty("passenger_count")]
-    [JsonPropertyName("passenger_count")]
-    public int PassengerCount { get; set; }
-
-    /// <summary>
-    /// 舱位类型
-    /// </summary>
-    [JsonProperty("cabin_class")]
-    [JsonPropertyName("cabin_class")]
-    public string CabinClass { get; set; }
-
-    /// <summary>
-    /// 订单状态:0待支付,1出票中,2已关闭,3有改签单,4有退票单,5出票成功,6退票申请中,7改签申请中
-    /// </summary>
-    [JsonProperty("status")]
-    [JsonPropertyName("status")]
-    public int Status { get; set; }
-
-    /// <summary>
-    /// 折扣
-    /// </summary>
-    [JsonProperty("discount")]
-    [JsonPropertyName("discount")]
-    public string Discount { get; set; }
-
-    /// <summary>
-    /// 航班号
-    /// </summary>
-    [JsonProperty("flight_no")]
-    [JsonPropertyName("flight_no")]
-    public string FlightNo { get; set; }
-
-    /// <summary>
-    /// 乘机人,多个用‘,’分割
-    /// </summary>
-    [JsonProperty("passenger_name")]
-    [JsonPropertyName("passenger_name")]
-    public string PassengerName { get; set; }
-
-    /// <summary>
-    /// 出发机场
-    /// </summary>
-    [JsonProperty("dep_airport")]
-    [JsonPropertyName("dep_airport")]
-    public string DepAirport { get; set; }
-
-    /// <summary>
-    /// 到达机场
-    /// </summary>
-    [JsonProperty("arr_airport")]
-    [JsonPropertyName("arr_airport")]
-    public string ArrAirport { get; set; }
-
-    /// <summary>
-    /// 发票信息对象
-    /// </summary>
-    [JsonProperty("invoice")]
-    [JsonPropertyName("invoice")]
-    public InvoiceDomain Invoice { get; set; }
-
-    /// <summary>
-    /// 成本中心对象
-    /// </summary>
-    [JsonProperty("cost_center")]
-    [JsonPropertyName("cost_center")]
-    public CostCenterDomain CostCenter { get; set; }
-
-    /// <summary>
-    /// 价目信息
-    /// </summary>
-    [JsonProperty("price_info_list")]
-    [JsonPropertyName("price_info_list")]
-    public List<PriceInfoDomain> PriceInfoList { get; set; }
-
-    /// <summary>
-    /// 保险信息
-    /// </summary>
-    [JsonProperty("insureInfo_list")]
-    [JsonPropertyName("insureInfo_list")]
-    public List<InsureInfoDomain> InsureInfoList { get; set; }
-
-    /// <summary>
-    /// 第三方行程id
-    /// </summary>
-    [JsonProperty("thirdpart_itinerary_id")]
-    [JsonPropertyName("thirdpart_itinerary_id")]
-    public string ThirdpartItineraryId { get; set; }
-
-    /// <summary>
-    /// 出行人列表
-    /// </summary>
-    [JsonProperty("user_affiliate_list")]
-    [JsonPropertyName("user_affiliate_list")]
-    public List<UserAffiliateDomain> UserAffiliateList { get; set; }
-
-    /// <summary>
-    /// 第三方申请单ID
-    /// </summary>
-    [JsonProperty("thirdpart_apply_id")]
-    [JsonPropertyName("thirdpart_apply_id")]
-    public string ThirdpartApplyId { get; set; }
-
-    /// <summary>
-    /// 申请单名称
-    /// </summary>
-    [JsonProperty("btrip_title")]
-    [JsonPropertyName("btrip_title")]
-    public string BtripTitle { get; set; }
-
-    /// <summary>
-    /// 项目id
-    /// </summary>
-    [JsonProperty("project_id")]
-    [JsonPropertyName("project_id")]
-    public string ProjectId { get; set; }
-
-    /// <summary>
-    /// 项目code
-    /// </summary>
-    [JsonProperty("project_code")]
-    [JsonPropertyName("project_code")]
-    public string ProjectCode { get; set; }
-
-    /// <summary>
-    /// 项目名称
-    /// </summary>
-    [JsonProperty("project_title")]
-    [JsonPropertyName("project_title")]
-    public string ProjectTitle { get; set; }
-
-    /// <summary>
-    /// 第三方项目id
-    /// </summary>
-    [JsonProperty("third_part_project_id")]
-    [JsonPropertyName("third_part_project_id")]
-    public string ThirdPartProjectId { get; set; }
-}
-
-/// <summary>
-/// 发票信息对象
-/// </summary>
-public class InvoiceDomain
-{
-    /// <summary>
-    /// 商旅发票id
-    /// </summary>
-    [JsonProperty("id")]
-    [JsonPropertyName("id")]
-    public long Id { get; set; }
-
-    /// <summary>
-    /// 发票抬头
-    /// </summary>
-    [JsonProperty("title")]
-    [JsonPropertyName("title")]
-    public string Title { get; set; }
-}
-
-/// <summary>
-/// 成本中心对象
-/// </summary>
-public class CostCenterDomain
-{
-    /// <summary>
-    /// 商旅成本中心id
-    /// </summary>
-    [JsonProperty("id")]
-    [JsonPropertyName("id")]
-    public long Id { get; set; }
-
-    /// <summary>
-    /// 企业id
-    /// </summary>
-    [JsonProperty("corpid")]
-    [JsonPropertyName("corpid")]
-    public string CorpId { get; set; }
-
-    /// <summary>
-    /// 成本中心编号
-    /// </summary>
-    [JsonProperty("number")]
-    [JsonPropertyName("number")]
-    public string Number { get; set; }
-
-    /// <summary>
-    /// 成本中心名称
-    /// </summary>
-    [JsonProperty("name")]
-    [JsonPropertyName("name")]
-    public string Name { get; set; }
-}
-
-/// <summary>
-/// 价目信息
-/// </summary>
-public class PriceInfoDomain
-{
-    /// <summary>
-    /// 价格
-    /// </summary>
-    [JsonProperty("price")]
-    [JsonPropertyName("price")]
-    public decimal Price { get; set; }
-
-    /// <summary>
-    /// 资金流向:1:支出,2:收入
-    /// </summary>
-    [JsonProperty("type")]
-    [JsonPropertyName("type")]
-    public AliTripPriceTypeEnum Type { get; set; }
-
-    /// <summary>
-    /// 交易类目
-    /// </summary>
-    [JsonProperty("category")]
-    [JsonPropertyName("category")]
-    public string Category { get; set; }
-
-    /// <summary>
-    /// 结算方式:1:个人现付,2:企业现付,4:企业月结,8、企业预存
-    /// </summary>
-    [JsonProperty("pay_type")]
-    [JsonPropertyName("pay_type")]
-    public AliTripPricePayTypeEnum PayType { get; set; }
-
-    /// <summary>
-    /// 流水创建时间
-    /// </summary>
-    [JsonProperty("gmt_create")]
-    [JsonPropertyName("gmt_create")]
-    public DateTime GmtCreate { get; set; }
-
-    /// <summary>
-    /// 乘机人,多个用‘,’分割
-    /// </summary>
-    [JsonProperty("passenger_name")]
-    [JsonPropertyName("passenger_name")]
-    public string PassengerName { get; set; }
-
-    /// <summary>
-    /// 流水单号
-    /// </summary>
-    [JsonProperty("tradeId")]
-    [JsonPropertyName("tradeId")]
-    public string TradeId { get; set; }
-
-    /// <summary>
-    /// 改签票号
-    /// </summary>
-    [JsonProperty("ticket_no")]
-    [JsonPropertyName("ticket_no")]
-    public string TicketNo { get; set; }
-
-    /// <summary>
-    /// 改签前的票号
-    /// </summary>
-    [JsonProperty("original_ticket_no")]
-    [JsonPropertyName("original_ticket_no")]
-    public string OriginalTicketNo { get; set; }
-
-    /// <summary>
-    /// 改签航班号
-    /// </summary>
-    [JsonProperty("changeFlightNo")]
-    [JsonPropertyName("changeFlightNo")]
-    public string ChangeFlightNo { get; set; }
-
-    /// <summary>
-    /// 改签折扣
-    /// </summary>
-    [JsonProperty("discount")]
-    [JsonPropertyName("discount")]
-    public string Discount { get; set; }
-
-    /// <summary>
-    /// 改签航班起飞时间
-    /// </summary>
-    [JsonProperty("startTime")]
-    [JsonPropertyName("startTime")]
-    public DateTime StartTime { get; set; }
-
-    /// <summary>
-    /// 改签航班到达时间
-    /// </summary>
-    [JsonProperty("endTime")]
-    [JsonPropertyName("endTime")]
-    public DateTime EndTime { get; set; }
-}
-
-/// <summary>
-/// 保险信息
-/// </summary>
-public class InsureInfoDomain
-{
-    /// <summary>
-    /// 保单号
-    /// </summary>
-    [JsonProperty("insure_no")]
-    [JsonPropertyName("insure_no")]
-    public string InsureNo { get; set; }
-
-    /// <summary>
-    /// 状态:1已出保 2已退保
-    /// </summary>
-    [JsonProperty("status")]
-    [JsonPropertyName("status")]
-    public AliTripFlightOrdersInsureInfoStatusEnum Status { get; set; }
-
-    /// <summary>
-    /// 乘机人(保险人)姓名
-    /// </summary>
-    [JsonProperty("name")]
-    [JsonPropertyName("name")]
-    public string Name { get; set; }
-}
-
-/// <summary>
-/// 出行人列表
-/// </summary>
-public class UserAffiliateDomain
-{
-    /// <summary>
-    /// 出行人ID
-    /// </summary>
-    [JsonProperty("userid")]
-    [JsonPropertyName("userid")]
-    public string UserId { get; set; }
-
-    /// <summary>
-    /// 出行人名称
-    /// </summary>
-    [JsonProperty("user_name")]
-    [JsonPropertyName("user_name")]
-    public string UserName { get; set; }
-}
-
-/// <summary>
-/// 分页相关信息
-/// </summary>
-public class PageInfoDomain
-{
-    /// <summary>
-    /// 当前页
-    /// </summary>
-    [JsonProperty("page")]
-    [JsonPropertyName("page")]
-    public string Page { get; set; }
-
-    /// <summary>
-    /// 每页大小
-    /// </summary>
-    [JsonProperty("page_size")]
-    [JsonPropertyName("page_size")]
-    public string PageSize { get; set; }
-
-    /// <summary>
-    /// 总记录数
-    /// </summary>
-    [JsonProperty("total_number")]
-    [JsonPropertyName("total_number")]
-    public string TotalNumber { get; set; }
-}

+ 0 - 104
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/GetAlitripTrainOrdersRequest.cs

@@ -1,104 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.AliTrip.DTO;
-
-/// <summary>
-/// 获取企业火车票订单数据
-/// </summary>
-public class GetAliTripTrainOrdersRequest
-{
-    public GetAliTripTrainOrdersRequestDomain rq { get; set; }
-}
-
-public class GetAliTripTrainOrdersRequestDomain
-{
-    /// <summary>
-    /// 开始时间 2017-05-01 00:00:00
-    /// </summary>
-    [JsonProperty("start_time")]
-    [JsonPropertyName("start_time")]
-    public string? StartTime { get; set; }
-
-    /// <summary>
-    /// 商旅申请单id
-    /// </summary>
-    [JsonProperty("apply_id")]
-    [JsonPropertyName("apply_id")]
-    public string? ApplyId { get; set; }
-
-    /// <summary>
-    /// 页数,从1开始
-    /// </summary>
-    [JsonProperty("page")]
-    [JsonPropertyName("page")]
-    public int? Page { get; set; }
-
-    /// <summary>
-    /// 用户id
-    /// </summary>
-    [JsonProperty("userid")]
-    [JsonPropertyName("userid")]
-    public string? UserId { get; set; }
-
-    /// <summary>
-    /// 每页数据量,默认10,最高50
-    /// </summary>
-    [JsonProperty("page_size")]
-    [JsonPropertyName("page_size")]
-    public int? PageSize { get; set; }
-
-    /// <summary>
-    /// 部门id
-    /// </summary>
-    [JsonProperty("deptid")]
-    [JsonPropertyName("deptid")]
-    public string? Deptid { get; set; }
-
-    /// <summary>
-    /// 结束时间 2017-05-01 00:00:00
-    /// </summary>
-    [JsonProperty("end_time")]
-    [JsonPropertyName("end_time")]
-    public string? EndTime { get; set; }
-
-    /// <summary>
-    /// 企业id
-    /// </summary>
-    [JsonProperty("corpid")]
-    [JsonPropertyName("corpid")]
-    public string? Corpid { get; set; }
-
-    /// <summary>
-    /// 更新结束时间 2017-05-01 00:00:00
-    /// </summary>
-    [JsonProperty("update_end_time")]
-    [JsonPropertyName("update_end_time")]
-    public string? UpdateEndTime { get; set; }
-
-    /// <summary>
-    /// 更新开始时间 2017-05-01 00:00:00
-    /// </summary>
-    [JsonProperty("update_start_time")]
-    [JsonPropertyName("update_start_time")]
-    public string? UpdateStartTime { get; set; }
-
-    /// <summary>
-    /// false:仅搜索未报销的申请单
-    /// </summary>
-    [JsonProperty("all_apply")]
-    [JsonPropertyName("all_apply")]
-    public string? AllApply { get; set; }
-
-    /// <summary>
-    /// 第三方申请单ID
-    /// </summary>
-    [JsonProperty("thirdpart_apply_id")]
-    [JsonPropertyName("thirdpart_apply_id")]
-    public string? ThirdpartApplyId { get; set; }
-}

+ 0 - 299
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/DTO/GetAlitripTrainOrdersResponse.cs

@@ -1,299 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Admin.NET.Plugin.DingTalk.RequestProxy.BaseTypes;
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.AliTrip.DTO;
-
-/// <summary>
-/// 获取企业火车票订单数据
-/// </summary>
-public class GetAliTripTrainOrdersResponse : DingtalkResponseErrorSuccess
-{
-    /// <summary>
-    /// 机票列表
-    /// </summary>
-    [JsonProperty("train_order_list")]
-    [JsonPropertyName("train_order_list")]
-    public List<TrainOrder> TrainOrderList { get; set; }
-}
-
-public class TrainOrder
-{
-    /// <summary>
-    /// 机票订单id
-    /// </summary>
-    [JsonProperty("id")]
-    [JsonPropertyName("id")]
-    public long Id { get; set; }
-
-    /// <summary>
-    /// 创建时间
-    /// </summary>
-    [JsonProperty("gmt_create")]
-    [JsonPropertyName("gmt_create")]
-    public DateTime GmtCreate { get; set; }
-
-    /// <summary>
-    /// 更新时间
-    /// </summary>
-    [JsonProperty("gmt_modified")]
-    [JsonPropertyName("gmt_modified")]
-    public DateTime GmtModified { get; set; }
-
-    /// <summary>
-    /// 企业id
-    /// </summary>
-    [JsonProperty("corpid")]
-    [JsonPropertyName("corpid")]
-    public string CorpId { get; set; }
-
-    /// <summary>
-    /// 企业名称
-    /// </summary>
-    [JsonProperty("corp_name")]
-    [JsonPropertyName("corp_name")]
-    public string CorpName { get; set; }
-
-    /// <summary>
-    /// 用户id
-    /// </summary>
-    [JsonProperty("userid")]
-    [JsonPropertyName("userid")]
-    public string UserId { get; set; }
-
-    /// <summary>
-    /// 用户姓名
-    /// </summary>
-    [JsonProperty("user_name")]
-    [JsonPropertyName("user_name")]
-    public string UserName { get; set; }
-
-    /// <summary>
-    /// 部门id
-    /// </summary>
-    [JsonProperty("deptid")]
-    [JsonPropertyName("deptid")]
-    public string DeptId { get; set; }
-
-    /// <summary>
-    /// 部门名称
-    /// </summary>
-    [JsonProperty("dept_name")]
-    [JsonPropertyName("dept_name")]
-    public string DeptName { get; set; }
-
-    /// <summary>
-    /// 商旅申请单id
-    /// </summary>
-    [JsonProperty("apply_id")]
-    [JsonPropertyName("apply_id")]
-    public string ApplyId { get; set; }
-
-    /// <summary>
-    /// 联系人
-    /// </summary>
-    [JsonProperty("contact_name")]
-    [JsonPropertyName("contact_name")]
-    public string ContactName { get; set; }
-
-    /// <summary>
-    /// 出发站
-    /// </summary>
-    [JsonProperty("dep_station")]
-    [JsonPropertyName("dep_station")]
-    public string DepStation { get; set; }
-
-    /// <summary>
-    /// 到达站
-    /// </summary>
-    [JsonProperty("arr_station")]
-    [JsonPropertyName("arr_station")]
-    public string ArrStation { get; set; }
-
-    /// <summary>
-    /// 出发时间
-    /// </summary>
-    [JsonProperty("dep_time")]
-    [JsonPropertyName("dep_time")]
-    public DateTime DepTime { get; set; }
-
-    /// <summary>
-    /// 到达时间
-    /// </summary>
-    [JsonProperty("arr_time")]
-    [JsonPropertyName("arr_time")]
-    public DateTime ArrTime { get; set; }
-
-    /// <summary>
-    /// 车次
-    /// </summary>
-    [JsonProperty("train_number")]
-    [JsonPropertyName("train_number")]
-    public string TrainNumber { get; set; }
-
-    /// <summary>
-    /// 车次类型
-    /// </summary>
-    [JsonProperty("train_type")]
-    [JsonPropertyName("train_type")]
-    public string TrainType { get; set; }
-
-    /// <summary>
-    /// 座位类型
-    /// </summary>
-    [JsonProperty("seat_type")]
-    [JsonPropertyName("seat_type")]
-    public string SeatType { get; set; }
-
-    /// <summary>
-    /// 运行时长
-    /// </summary>
-    [JsonProperty("run_time")]
-    [JsonPropertyName("run_time")]
-    public string RunTime { get; set; }
-
-    /// <summary>
-    /// 12306票号
-    /// </summary>
-    [JsonProperty("ticket_no_12306")]
-    [JsonPropertyName("ticket_no_12306")]
-    public string TicketNo12306 { get; set; }
-
-    /// <summary>
-    /// 出发城市
-    /// </summary>
-    [JsonProperty("dep_city")]
-    [JsonPropertyName("dep_city")]
-    public string DepCity { get; set; }
-
-    /// <summary>
-    /// 到达城市
-    /// </summary>
-    [JsonProperty("arr_city")]
-    [JsonPropertyName("arr_city")]
-    public string ArrCity { get; set; }
-
-    /// <summary>
-    /// 乘客姓名
-    /// </summary>
-    [JsonProperty("rider_name")]
-    [JsonPropertyName("rider_name")]
-    public string RiderName { get; set; }
-
-    /// <summary>
-    /// 票的数量
-    /// </summary>
-    [JsonProperty("ticket_count")]
-    [JsonPropertyName("ticket_count")]
-    public int TicketCount { get; set; }
-
-    /// <summary>
-    /// 订单状态:0:待支付,1:出票中,2:已关闭,3:改签成功,4:退票成功,5:出票完成,6:退票申请中,7:改签申请中,8:已出票/已发货,9:出票失败,10:改签失败,11:退票失败
-    /// </summary>
-    [JsonProperty("status")]
-    [JsonPropertyName("status")]
-    public AliTripTrainOrdersStatusEnum Status { get; set; }
-
-    /// <summary>
-    /// 发票信息对象
-    /// </summary>
-    [JsonProperty("invoice")]
-    [JsonPropertyName("invoice")]
-    public InvoiceDomain Invoice { get; set; }
-
-    /// <summary>
-    /// 成本中心对象
-    /// </summary>
-    [JsonProperty("cost_center")]
-    [JsonPropertyName("cost_center")]
-    public CostCenterDomain CostCenter { get; set; }
-
-    /// <summary>
-    /// 价目信息
-    /// </summary>
-    [JsonProperty("price_info_list")]
-    [JsonPropertyName("price_info_list")]
-    public TrainPriceInfo PriceInfoList { get; set; }
-
-    /// <summary>
-    /// 第三方行程id
-    /// </summary>
-    [JsonProperty("thirdpart_itinerary_id")]
-    [JsonPropertyName("thirdpart_itinerary_id")]
-    public string ThirdpartItineraryId { get; set; }
-
-    /// <summary>
-    /// 出行人列表
-    /// </summary>
-    [JsonProperty("user_affiliate_list")]
-    [JsonPropertyName("user_affiliate_list")]
-    public List<UserAffiliateDomain> UserAffiliateList { get; set; }
-
-    /// <summary>
-    /// 第三方申请单ID
-    /// </summary>
-    [JsonProperty("thirdpart_apply_id")]
-    [JsonPropertyName("thirdpart_apply_id")]
-    public string ThirdpartApplyId { get; set; }
-
-    /// <summary>
-    /// 申请单名称
-    /// </summary>
-    [JsonProperty("btrip_title")]
-    [JsonPropertyName("btrip_title")]
-    public string BtripTitle { get; set; }
-}
-
-/// <summary>
-/// 价目信息
-/// </summary>
-public class TrainPriceInfo
-{
-    /// <summary>
-    /// 价格
-    /// </summary>
-    [JsonProperty("price")]
-    [JsonPropertyName("price")]
-    public decimal Price { get; set; }
-
-    /// <summary>
-    /// 资金流向:1:支出,2:收入
-    /// </summary>
-    [JsonProperty("type")]
-    [JsonPropertyName("type")]
-    public AliTripPriceTypeEnum Type { get; set; }
-
-    /// <summary>
-    /// 交易类目
-    /// </summary>
-    [JsonProperty("category")]
-    [JsonPropertyName("category")]
-    public string Category { get; set; }
-
-    /// <summary>
-    /// 结算方式:1:个人现付,2:企业现付,4:企业月结,8、企业预存
-    /// </summary>
-    [JsonProperty("pay_type")]
-    [JsonPropertyName("pay_type")]
-    public AliTripPricePayTypeEnum PayType { get; set; }
-
-    /// <summary>
-    /// 流水创建时间
-    /// </summary>
-    [JsonProperty("gmt_create")]
-    [JsonPropertyName("gmt_create")]
-    public DateTime GmtCreate { get; set; }
-
-    /// <summary>
-    /// 乘机人,多个用‘,’分割
-    /// </summary>
-    [JsonProperty("passenger_name")]
-    [JsonPropertyName("passenger_name")]
-    public string PassengerName { get; set; }
-}

+ 0 - 21
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/AliTrip/IAliTripRequestProxy.cs

@@ -1,21 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Admin.NET.Plugin.DingTalk.RequestProxy.AliTrip.DTO;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.AliTrip;
-
-public interface IAliTripRequestProxy : IHttpDispatchProxy
-{
-    [Post("https://oapi.dingtalk.com/topapi/alitrip/btrip/address/get"), JsonSerialization(typeof(NewtonsoftJsonSerializerProvider))]
-    Task<string> GetAliTripAddress([QueryString("access_token")] string accessToken, [Body] GetAliTripAddressRequest request);
-
-    [Post("https://oapi.dingtalk.com/topapi/alitrip/btrip/flight/order/search"), JsonSerialization(typeof(NewtonsoftJsonSerializerProvider))]
-    Task<string> GetAliTripFlightOrders([QueryString("access_token")] string accessToken, GetAliTripFlightOrdersRequest rq);
-
-    [Post("https://oapi.dingtalk.com/topapi/alitrip/btrip/train/order/search"), JsonSerialization(typeof(NewtonsoftJsonSerializerProvider))]
-    Task<string> GetAliTripTrainOrders([QueryString("access_token")] string accessToken, GetAliTripTrainOrdersRequest rq);
-}

+ 0 - 166
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/AttendanceRequest.cs

@@ -1,166 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Admin.NET.Plugin.DingTalk.RequestProxy.Attendance.DTO;
-
-using NewLife;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.Attendance;
-
-public class AttendanceRequest : IScoped
-{
-    private readonly IAttendanceRequestProxy _request;
-
-    public AttendanceRequest(IAttendanceRequestProxy request)
-    {
-        _request = request;
-    }
-
-    /// <summary>
-    /// 获取打卡详情
-    /// </summary>
-    /// <param name="accessToken"></param>
-    /// <param name="useridList">企业内的员工ID列表,最大值50</param>
-    /// <param name="from">查询考勤打卡记录的起始工作日</param>
-    /// <param name="to">查询考勤打卡记录的结束工作日</param>
-    /// <param name="isI18N">是否为海外企业使用</param>
-    /// <returns></returns>
-    public async Task<ListRecordResponse> ListRecord(string accessToken, List<string> useridList, DateTime from, DateTime to, bool isI18N = false)
-    {
-        var requestBody = new ListRecordRequest
-        {
-            UserIds = useridList,
-            CheckDateFrom = from.ToString("yyyy-MM-dd HH:mm:ss"),
-            CheckDateTo = to.ToString("yyyy-MM-dd HH:mm:ss"),
-            IsI18n = isI18N
-        };
-        var resStr = await _request.ListRecord(accessToken, requestBody);
-        var res = resStr.ToObject<ListRecordResponse>();
-        return res;
-    }
-
-    /// <summary>
-    /// 通知审批通过
-    /// </summary>
-    /// <param name="accessToken"></param>
-    /// <param name="userid">员工的userId</param>
-    /// <param name="approveId">审批单ID,最大长度100个字符,自定义值</param>
-    /// <param name="tagName">审批单类型名称,最大长度20个字符。 支持类型如下:请假, 出差,外出,加班</param>
-    /// <param name="jumpUrl">审批单跳转地址,最大长度200个字符</param>
-    /// <param name="bizType">审批单类型,可取值:1:加班,2:出差、外出,3:请假</param>
-    /// <param name="fromTime">开始时间。开始时间不能早于当前时间前31天</param>
-    /// <param name="toTime">结束时间</param>
-    /// <param name="durationUnit">时长单位,支持格式如下:day,halfDay,hour:biz_type为1时仅支持hour</param>
-    /// <param name="calculateModel">计算方法:0:按自然日计算,1:按工作日计算</param>
-    /// <param name="leaveCode">假期规则唯一标识。选填。仅支持bizType=3 请假时传不为空,可以支持根据假期类型设置的取整规则进行时长取整</param>
-    /// <param name="subType">子类型名称,最大长度20个字符。审批单类型biz_type=3时,该参数必传。</param>
-    /// <param name="overTimeDuration">biz_type为1时必传,加班时长单位小时</param>
-    /// <param name="overTimeToMore">biz_type为1时必传:1:加班转调休,2:加班转工资</param>
-    /// <returns></returns>
-    public async Task<AttendanceApprovalsFinishResponse> ApproveFinish(string accessToken, string userid, string approveId, string tagName, string jumpUrl,
-        AttendanceBizTypeEnum bizType, DateTime fromTime, DateTime toTime, AttendanceDurationUnitEnum durationUnit, AttendanceCalculateModelEnum calculateModel, string? leaveCode = null,
-        string? subType = null, string? overTimeDuration = null, long? overTimeToMore = null)
-    {
-        if (bizType == AttendanceBizTypeEnum.请假 && string.IsNullOrEmpty(subType))
-            throw Oops.Oh("审批单类型biz_type=3时,subType必传。");
-        if (bizType == AttendanceBizTypeEnum.加班 && (string.IsNullOrEmpty(overTimeDuration) || !overTimeToMore.HasValue))
-            throw Oops.Oh("biz_type为1时,overTimeDuration和overTimeToMore必传");
-        var fromTimeStr = "";
-        var toTimeStr = "";
-        switch (durationUnit)
-        {
-            case AttendanceDurationUnitEnum.Day:
-                fromTimeStr = fromTime.ToString("yyyy-MM-dd");
-                toTimeStr = toTime.ToString("yyyy-MM-dd");
-                break;
-
-            case AttendanceDurationUnitEnum.HalfDay:
-                fromTimeStr = TimeToHalfDay(fromTime);
-                toTimeStr = TimeToHalfDay(toTime);
-                break;
-
-            case AttendanceDurationUnitEnum.Hour:
-                fromTimeStr = fromTime.ToString("yyyy-MM-dd HH:mm");
-                toTimeStr = toTime.ToString("yyyy-MM-dd HH:mm");
-                break;
-        }
-        var requestBody = new AttdendanceApprovalsFinishRequest
-        {
-            ApproveId = approveId,
-            TagName = tagName,
-            JumpUrl = jumpUrl,
-            OvertimeDuration = overTimeDuration,
-            OverTimeToMore = overTimeToMore,
-            SubType = subType,
-            TopCalculateApproveDurationParam = new TopCalculateApproveDurationParamDomain
-            {
-                BizType = bizType,
-                FromTime = fromTimeStr,
-                ToTime = toTimeStr,
-                DurationUnit = durationUnit.ToString(),
-                CalculateModel = (long)calculateModel,
-                LeaveCode = leaveCode
-            }
-        };
-        try
-        {
-            var resStr = await _request.ApprovalsFinish(userid, accessToken, requestBody);
-            var res = resStr.ToObject<AttendanceApprovalsFinishResponse>();
-            return res;
-        }
-        catch (Exception ex)
-        {
-            throw Oops.Oh(ex.Message);
-        }
-    }
-
-    /// <summary>
-    /// 通知审批撤销
-    /// </summary>
-    /// <param name="accessToken"></param>
-    /// <param name="userid">员工的userId</param>
-    /// <param name="approveId">审批ID</param>
-    /// <returns></returns>
-    public async Task<AttendanceApprovelsCancelResponse> ApproveCancel(string accessToken, string userid, string approveId)
-    {
-        var resStr = await _request.ApprovalsCancel(accessToken, new AttendanceApprovelsCancelRequest
-        {
-            ApproveId = approveId,
-            UserId = userid,
-        });
-        return resStr.ToObject<AttendanceApprovelsCancelResponse>();
-    }
-
-    /// <summary>
-    /// 查询请假状态
-    /// </summary>
-    /// <param name="accessToken"></param>
-    /// <param name="userIdList">待查询用户的ID列表,每次最多100个</param>
-    /// <param name="startTime">开始时间 ,支持最多180天的查询</param>
-    /// <param name="endTime">结束时间,支持最多180天的查询</param>
-    /// <param name="offset">支持分页查询,与size参数同时设置时才生效,此参数代表偏移量,偏移量从0开始</param>
-    /// <param name="size">支持分页查询,与offset参数同时设置时才生效,此参数代表分页大小,最大20</param>
-    /// <returns></returns>
-    public async Task<GetLeaveStatusResponse> GetLeaveStatus(string accessToken, List<string> userIdList, DateTime startTime, DateTime endTime, int offset = 0, int size = 10)
-    {
-        var resStr = await _request.GetLeaveStatus(accessToken, new GetLeaveStatusRequest
-        {
-            UserIdList = userIdList.Join(),
-            StartTime = new DateTimeOffset(startTime).ToUnixTimeMilliseconds(),
-            EndTime = new DateTimeOffset(endTime).ToUnixTimeMilliseconds(),
-            Offset = offset,
-            Size = size
-        });
-        return resStr.ToObject<GetLeaveStatusResponse>();
-    }
-
-    private string TimeToHalfDay(DateTime time)
-    {
-        var dateStr = time.ToString("yyyy-MM-dd");
-        var apm = time.Hour < 12 ? "AM" : "PM";
-        return dateStr + " " + apm;
-    }
-}

+ 0 - 109
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/AttdendanceApprovalsFinishRequest.cs

@@ -1,109 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.Attendance.DTO;
-
-/// <summary>
-/// 出勤审批完成请求入参
-/// </summary>
-public class AttdendanceApprovalsFinishRequest
-{
-    /// <summary>
-    /// 审批单类型名称,最大长度20个字符。 支持类型如下: 请假,出差,外出,加班
-    /// </summary>
-    [JsonProperty("tagName")]
-    [JsonPropertyName("tagName")]
-    public string TagName;
-
-    /// <summary>
-    /// 子类型名称,最大长度20个字符。审批单类型biz_type=3时,该参数必传。
-    /// </summary>
-    [JsonProperty("subType")]
-    [JsonPropertyName("subType")]
-    public string? SubType;
-
-    /// <summary>
-    /// 审批单ID,最大长度100个字符,自定义值。
-    /// </summary>
-    [JsonProperty("approveId")]
-    [JsonPropertyName("approveId")]
-    public string ApproveId;
-
-    /// <summary>
-    /// 审批单跳转地址,最大长度200个字符。
-    /// </summary>
-    [JsonProperty("jumpUrl")]
-    [JsonPropertyName("jumpUrl")]
-    public string JumpUrl;
-
-    /// <summary>
-    /// biz_type为1时必传,加班时长单位小时。
-    /// </summary>
-    [JsonProperty("overtimeDuration")]
-    [JsonPropertyName("overtimeDuration")]
-    public string? OvertimeDuration;
-
-    /// <summary>
-    /// biz_type为1时必传:1:加班转调休,2:加班转工资
-    /// </summary>
-    [JsonProperty("overTimeToMore")]
-    [JsonPropertyName("overTimeToMore")]
-    public long? OverTimeToMore;
-
-    /// <summary>
-    /// 时长相关入参。
-    /// </summary>
-    [JsonProperty("topCalculateApproveDurationParam")]
-    [JsonPropertyName("topCalculateApproveDurationParam")]
-    public TopCalculateApproveDurationParamDomain TopCalculateApproveDurationParam;
-}
-
-public class TopCalculateApproveDurationParamDomain
-{
-    /// <summary>
-    /// 审批单类型,可取值:1:加班,2:出差、外出,3:请假
-    /// </summary>
-    [JsonProperty("bizType")]
-    [JsonPropertyName("bizType")]
-    public AttendanceBizTypeEnum BizType;
-
-    /// <summary>
-    /// 开始时间。开始时间不能早于当前时间前31天。 支持以下格式:2019-08-15,2019-08-15 AM,2019-08-15 12:43
-    /// </summary>
-    [JsonProperty("fromTime")]
-    [JsonPropertyName("fromTime")]
-    public string FromTime;
-
-    /// <summary>
-    /// 结束时间。结束时间减去开始时间的天数不能超过31天。biz_type为1时,结束时间减去开始时间的天数不能超过1天。
-    /// </summary>
-    [JsonProperty("toTime")]
-    [JsonPropertyName("toTime")]
-    public string ToTime;
-
-    /// <summary>
-    /// 时长单位,支持格式如下:day,halfDay,hour:biz_type为1时仅支持hour。
-    /// </summary>
-    [JsonProperty("durationUnit")]
-    [JsonPropertyName("durationUnit")]
-    public string DurationUnit;
-
-    /// <summary>
-    /// 计算方法:0:按自然日计算,1:按工作日计算
-    /// </summary>
-    [JsonProperty("calculateModel")]
-    [JsonPropertyName("calculateModel")]
-    public long CalculateModel;
-
-    /// <summary>
-    /// 假期规则唯一标识。选填。仅支持bizType=3 请假时传不为空,可以支持根据假期类型设置的取整规则进行时长取整。
-    /// </summary>
-    [JsonProperty("leaveCode")]
-    [JsonPropertyName("leaveCode")]
-    public string? LeaveCode;
-}

+ 0 - 55
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/AttendanceApprovalsFinishResponse.cs

@@ -1,55 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Admin.NET.Plugin.DingTalk.RequestProxy.BaseTypes;
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.Attendance.DTO;
-
-/// <summary>
-/// 出勤批准完成响应
-/// </summary>
-public class AttendanceApprovalsFinishResponse : DingtalkResponseSuccessResult<AttendanceApprovalsFinishResponseResult>
-{
-}
-
-/// <summary>
-/// 考勤批准完成响应结果
-/// </summary>
-public class AttendanceApprovalsFinishResponseResult
-{
-    /// <summary>
-    /// 总时长,该字段的单位与本企业内对应审批单设置的单位一致
-    /// </summary>
-    [JsonProperty("duration")]
-    [JsonPropertyName("duration")]
-    public double Duration;
-
-    /// <summary>
-    /// 详细信息
-    /// </summary>
-    [JsonProperty("durationDetail")]
-    [JsonPropertyName("durationDetail")]
-    public List<DurationDetailDomain> DurationDetail;
-}
-
-public class DurationDetailDomain
-{
-    /// <summary>
-    /// 审批通过日期
-    /// </summary>
-    [JsonProperty("date")]
-    [JsonPropertyName("date")]
-    public string Date;
-
-    /// <summary>
-    /// 每日时长
-    /// </summary>
-    [JsonProperty("duration")]
-    [JsonPropertyName("duration")]
-    public double Duration;
-}

+ 0 - 29
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/AttendanceApprovelsCancelRequest.cs

@@ -1,29 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.Attendance.DTO;
-
-/// <summary>
-/// 通知审批撤销
-/// </summary>
-public class AttendanceApprovelsCancelRequest
-{
-    /// <summary>
-    /// 用户id
-    /// </summary>
-    [JsonProperty("userid")]
-    [JsonPropertyName("userid")]
-    public string? UserId { get; set; }
-
-    /// <summary>
-    /// 审批ID。 企业内部应用,来自通知审批通过接口自定义的参数approve_id。 第三方企业应用,来自通知审批通过接口自定义的参数approve_id。
-    /// </summary>
-    [JsonProperty("approve_id")]
-    [JsonPropertyName("approve_id")]
-    public string ApproveId { get; set; }
-}

+ 0 - 16
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/AttendanceApprovelsCancelResponse.cs

@@ -1,16 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Admin.NET.Plugin.DingTalk.RequestProxy.BaseTypes;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.Attendance.DTO;
-
-/// <summary>
-/// 通知审批撤销
-/// </summary>
-public class AttendanceApprovelsCancelResponse : DingtalkResponseError
-{
-}

+ 0 - 39
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/Enums.cs

@@ -1,39 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.Attendance.DTO;
-
-/// <summary>
-/// 考勤审批单类型,可取值:1:加班,2:出差、外出,3:请假
-/// </summary>
-[SuppressSniffer]
-public enum AttendanceBizTypeEnum
-{
-    加班 = 1,
-    出差 = 2,
-    请假 = 3
-}
-
-/// <summary>
-/// 考勤审批单时长单位,支持格式如下:day,halfDay,hour:biz_type为1时仅支持hour
-/// </summary>
-[SuppressSniffer]
-public enum AttendanceDurationUnitEnum
-{
-    Day,
-    HalfDay,
-    Hour
-}
-
-/// <summary>
-/// 考勤审批单计算方法:0:按自然日计算,1:按工作日计算
-/// </summary>
-[SuppressSniffer]
-public enum AttendanceCalculateModelEnum
-{
-    按自然日计算 = 0,
-    按工作日计算 = 1
-}

+ 0 - 38
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/GetLeaveStatusRequest.cs

@@ -1,38 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Admin.NET.Plugin.DingTalk.RequestProxy.BaseTypes;
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.Attendance.DTO;
-
-/// <summary>
-/// 查询请假状态
-/// </summary>
-public class GetLeaveStatusRequest:DingtalkRequestOffsetSize
-{
-    /// <summary>
-    /// 待查询用户的ID列表,每次最多100个
-    /// </summary>
-    [JsonProperty("userid_list")]
-    [JsonPropertyName("userid_list")]
-    public string UserIdList { get; set; }
-
-    /// <summary>
-    /// 开始时间 ,Unix时间戳,支持最多180天的查询
-    /// </summary>
-    [JsonProperty("start_time")]
-    [JsonPropertyName("start_time")]
-    public long StartTime { get; set; }
-
-    /// <summary>
-    /// 结束时间,Unix时间戳,支持最多180天的查询
-    /// </summary>
-    [JsonProperty("end_time")]
-    [JsonPropertyName("end_time")]
-    public long EndTime { get; set; }
-}

+ 0 - 73
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/GetLeaveStatusResponse.cs

@@ -1,73 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Admin.NET.Plugin.DingTalk.RequestProxy.BaseTypes;
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.Attendance.DTO;
-
-/// <summary>
-/// 查询请假状态
-/// </summary>
-public class GetLeaveStatusResponse : DingTalkResponseAll<GetLeaveStatusResult>
-{
-}
-
-public class GetLeaveStatusResult
-{
-    /// <summary>
-    /// 是否还有更多的数据
-    /// </summary>
-    [JsonProperty("has_more")]
-    [JsonPropertyName("has_more")]
-    public bool HasMore { get; set; }
-
-    /// <summary>
-    /// 请假状态列表
-    /// </summary>
-    [JsonProperty("leave_status")]
-    [JsonPropertyName("leave_status")]
-    public List<LeaveStatusDomain> LeaveStatus { get; set; }
-}
-
-public class LeaveStatusDomain
-{
-    /// <summary>
-    /// 请假单位:    percent_day:天    percent_hour:小时
-    /// </summary>
-    [JsonProperty("duration_unit")]
-    [JsonPropertyName("duration_unit")]
-    public string DurationUnit { get; set; }
-
-    /// <summary>
-    /// 假期时长*100,例如用户请假时长为1天,该值就等于100。
-    /// </summary>
-    [JsonProperty("duration_percent")]
-    [JsonPropertyName("duration_percent")]
-    public double DurationPercent { get; set; }
-
-    /// <summary>
-    /// 请假结束时间,Unix时间戳。
-    /// </summary>
-    [JsonProperty("end_time")]
-    [JsonPropertyName("end_time")]
-    public long EndTime { get; set; }
-
-    /// <summary>
-    /// 请假开始时间,Unix时间戳。
-    /// </summary>
-    [JsonProperty("start_time")]
-    [JsonPropertyName("start_time")]
-    public long StartTime { get; set; }
-
-    /// <summary>
-    /// 用户ID。
-    /// </summary>
-    [JsonProperty("userid")]
-    [JsonPropertyName("userid")]
-    public string UserId { get; set; }
-}

+ 0 - 43
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/ListRecordRequest.cs

@@ -1,43 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.Attendance.DTO;
-
-/// <summary>
-/// 获取打卡详情
-/// </summary>
-public class ListRecordRequest
-{
-    /// <summary>
-    /// 企业内的员工ID列表,最大值50
-    /// </summary>
-    [JsonProperty("userIds")]
-    [JsonPropertyName("userIds")]
-    public List<string> UserIds { get; set; }
-
-    /// <summary>
-    /// 查询考勤打卡记录的起始工作日。格式为:yyyy-MM-dd hh:mm:ss。例如,参数传"2021-12-01 10:00:00",员工在09:00的打卡信息获取不到。
-    /// </summary>
-    [JsonProperty("checkDateFrom")]
-    [JsonPropertyName("checkDateFrom")]
-    public string CheckDateFrom { get; set; }
-
-    /// <summary>
-    /// 查询考勤打卡记录的结束工作日。格式为:yyyy-MM-dd hh:mm:ss。例如,参数传"2021-12-01 18:00:00",员工在19:00的打卡信息获取不到。
-    /// </summary>
-    [JsonProperty("checkDateTo")]
-    [JsonPropertyName("checkDateTo")]
-    public string CheckDateTo { get; set; }
-
-    /// <summary>
-    /// 是否为海外企业使用: true:海外平台使用。false(默认):国内平台使用
-    /// </summary>
-    [JsonProperty("isI18n")]
-    [JsonPropertyName("isI18n")]
-    public bool IsI18n { get; set; } = false;
-}

+ 0 - 294
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/DTO/ListRecordResponse.cs

@@ -1,294 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Admin.NET.Plugin.DingTalk.RequestProxy.BaseTypes;
-
-using System.Text.Json.Serialization;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.Attendance.DTO;
-
-/// <summary>
-/// 获取打卡详情
-/// </summary>
-public class ListRecordResponse : DingtalkResponseError
-{
-    /// <summary>
-    /// 打卡详情
-    /// </summary>
-    [JsonProperty("recordresult")]
-    [JsonPropertyName("recordresult")]
-    public List<ListRecordResultDomain> RecordResult { get; set; }
-}
-
-public class ListRecordResultDomain
-{
-    /// <summary>
-    /// 关联的审批ID,当该字段非空时,表示打卡记录与请假、加班等审批有关
-    /// </summary>
-    [JsonPropertyName("approveId")]
-    [JsonProperty("approveId")]
-    public long ApproveId { get; set; }
-
-    /// <summary>
-    /// 基准定位精度
-    /// </summary>
-    [JsonProperty("baseAccuracy")]
-    [JsonPropertyName("baseAccuracy")]
-    public string BaseAccuracy { get; set; }
-
-    /// <summary>
-    /// 基准地址
-    /// </summary>
-    [JsonProperty("baseAddress")]
-    [JsonPropertyName("baseAddress")]
-    public string BaseAddress { get; set; }
-
-    /// <summary>
-    /// 计算迟到和早退,基准时间;也可作为排班打卡时间
-    /// </summary>
-    [JsonProperty("baseCheckTime")]
-    [JsonPropertyName("baseCheckTime")]
-    public string BaseCheckTime { get; set; }
-
-    /// <summary>
-    /// 基准纬度
-    /// </summary>
-    [JsonProperty("baseLatitude")]
-    [JsonPropertyName("baseLatitude")]
-    public string BaseLatitude { get; set; }
-
-    /// <summary>
-    /// 基准经度
-    /// </summary>
-    [JsonProperty("baseLongitude")]
-    [JsonPropertyName("baseLongitude")]
-    public string BaseLongitude { get; set; }
-
-    /// <summary>
-    /// 基准MAC地址
-    /// </summary>
-    [JsonProperty("baseMacAddr")]
-    [JsonPropertyName("baseMacAddr")]
-    public string BaseMacAddr { get; set; }
-
-    /// <summary>
-    /// 基准wifi ssid
-    /// </summary>
-    [JsonProperty("baseSsid")]
-    [JsonPropertyName("baseSsid")]
-    public string BaseSsid { get; set; }
-
-    /// <summary>
-    /// 关联的业务ID
-    /// </summary>
-    [JsonProperty("bizId")]
-    [JsonPropertyName("bizId")]
-    public string BizId { get; set; }
-
-    /// <summary>
-    /// 考勤类型:OnDuty:上班,OffDuty:下班
-    /// </summary>
-    [JsonProperty("checkType")]
-    [JsonPropertyName("checkType")]
-    public string CheckType { get; set; }
-
-    /// <summary>
-    /// 班次ID
-    /// </summary>
-    [JsonProperty("classId")]
-    [JsonPropertyName("classId")]
-    public long ClassId { get; set; }
-
-    /// <summary>
-    /// 打卡设备ID
-    /// </summary>
-    [JsonProperty("deviceId")]
-    [JsonPropertyName("deviceId")]
-    public string DeviceId { get; set; }
-
-    /// <summary>
-    /// 打卡设备序列号
-    /// </summary>
-    [JsonProperty("deviceSN")]
-    [JsonPropertyName("deviceSN")]
-    public string DeviceSN { get; set; }
-
-    /// <summary>
-    /// 打卡记录创建时间
-    /// </summary>
-    [JsonProperty("gmtCreate")]
-    [JsonPropertyName("gmtCreate")]
-    public string GmtCreate { get; set; }
-
-    /// <summary>
-    /// 打卡记录修改时间
-    /// </summary>
-    [JsonProperty("gmtModified")]
-    [JsonPropertyName("gmtModified")]
-    public string GmtModified { get; set; }
-
-    /// <summary>
-    /// 考勤组ID
-    /// </summary>
-    [JsonProperty("groupId")]
-    [JsonPropertyName("groupId")]
-    public long GroupId { get; set; }
-
-    /// <summary>
-    /// 考勤ID
-    /// </summary>
-    [JsonProperty("id")]
-    [JsonPropertyName("id")]
-    public long Id { get; set; }
-
-    /// <summary>
-    /// 对应的invalidRecordType异常信息的具体描述
-    /// </summary>
-    [JsonProperty("invalidRecordMsg")]
-    [JsonPropertyName("invalidRecordMsg")]
-    public string InvalidRecordMsg { get; set; }
-
-    /// <summary>
-    /// 异常信息类型: Security:安全相关原因,Other:其他原因
-    /// </summary>
-    [JsonProperty("invalidRecordType")]
-    [JsonPropertyName("invalidRecordType")]
-    public string InvalidRecordType { get; set; }
-
-    /// <summary>
-    /// 是否合法.Y:合法,N:不合法
-    /// </summary>
-    [JsonProperty("isLegal")]
-    [JsonPropertyName("isLegal")]
-    public string IsLegal { get; set; }
-
-    /// <summary>
-    /// 定位方法
-    /// </summary>
-    [JsonProperty("locationMethod")]
-    [JsonPropertyName("locationMethod")]
-    public string LocationMethod { get; set; }
-
-    /// <summary>
-    /// 位置结果:Normal:范围内,Outside:范围外,NotSigned:未打卡
-    /// </summary>
-    [JsonProperty("locationResult")]
-    [JsonPropertyName("locationResult")]
-    public string LocationResult { get; set; }
-
-    /// <summary>
-    /// 打卡备注
-    /// </summary>
-    [JsonProperty("outsideRemark")]
-    [JsonPropertyName("outsideRemark")]
-    public string OutsideRemark { get; set; }
-
-    /// <summary>
-    /// 排班打卡时间
-    /// </summary>
-    [JsonProperty("planCheckTime")]
-    [JsonPropertyName("planCheckTime")]
-    public string PlanCheckTime { get; set; }
-
-    /// <summary>
-    /// 排班ID
-    /// </summary>
-    [JsonProperty("planId")]
-    [JsonPropertyName("planId")]
-    public long PlanId { get; set; }
-
-    /// <summary>
-    /// 关联的审批实例ID,当该字段非空时,表示打卡记录与请假、加班等审批有关
-    /// </summary>
-    [JsonProperty("procInstId")]
-    [JsonPropertyName("procInstId")]
-    public string ProcInstId { get; set; }
-
-    /// <summary>
-    /// 数据来源:ATM:考勤机打卡(指纹/人脸打卡),BEACON:IBeacon,DING_ATM:钉钉考勤机(考勤机蓝牙打卡),USER:用户打卡
-    ///         BOSS:老板改签,APPROVE:审批系统,SYSTEM:考勤系统,AUTO_CHECK:自动打卡
-    /// </summary>
-    [JsonProperty("sourceType")]
-    [JsonPropertyName("sourceType")]
-    public string SourceType { get; set; }
-
-    /// <summary>
-    /// 打卡结果。 Normal:正常,Early:早退,Late:迟到,SeriousLate:严重迟到,Absenteeism:旷工迟到,NotSigned:未打卡
-    /// </summary>
-    [JsonProperty("timeResult")]
-    [JsonPropertyName("timeResult")]
-    public string TimeResult { get; set; }
-
-    /// <summary>
-    /// 用户打卡定位精度
-    /// </summary>
-    [JsonProperty("userAccuracy")]
-    [JsonPropertyName("userAccuracy")]
-    public string UserAccuracy { get; set; }
-
-    /// <summary>
-    /// 用户打卡地址
-    /// </summary>
-    [JsonProperty("userAddress")]
-    [JsonPropertyName("userAddress")]
-    public string UserAddress { get; set; }
-
-    /// <summary>
-    /// 实际打卡时间
-    /// </summary>
-    [JsonProperty("userCheckTime")]
-    [JsonPropertyName("userCheckTime")]
-    public string UserCheckTime { get; set; }
-
-    /// <summary>
-    /// 打卡人的userId
-    /// </summary>
-    [JsonProperty("userId")]
-    [JsonPropertyName("userId")]
-    public string UserId { get; set; }
-
-    /// <summary>
-    /// 用户打卡纬度
-    /// </summary>
-    [JsonProperty("userLatitude")]
-    [JsonPropertyName("userLatitude")]
-    public string UserLatitude { get; set; }
-
-    /// <summary>
-    /// 用户打卡经度
-    /// </summary>
-    [JsonProperty("userLongitude")]
-    [JsonPropertyName("userLongitude")]
-    public string UserLongitude { get; set; }
-
-    /// <summary>
-    /// 用户打卡wifi Mac地址
-    /// </summary>
-    [JsonProperty("userMacAddr")]
-    [JsonPropertyName("userMacAddr")]
-    public string UserMacAddr { get; set; }
-
-    /// <summary>
-    /// 用户打卡wifi SSID
-    /// </summary>
-    [JsonProperty("userSsid")]
-    [JsonPropertyName("userSsid")]
-    public string UserSsid { get; set; }
-
-    /// <summary>
-    /// 工作日
-    /// </summary>
-    [JsonProperty("workDate")]
-    [JsonPropertyName("workDate")]
-    public long WorkDate { get; set; }
-
-    /// <summary>
-    /// 企业ID
-    /// </summary>
-    [JsonProperty("corpId")]
-    [JsonPropertyName("corpId")]
-    public string CorpId { get; set; }
-}

+ 0 - 25
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/RequestProxy/Attendance/IAttendanceRequestProxy.cs

@@ -1,25 +0,0 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Admin.NET.Plugin.DingTalk.RequestProxy.Attendance.DTO;
-
-namespace Admin.NET.Plugin.DingTalk.RequestProxy.Attendance;
-
-public interface IAttendanceRequestProxy : IHttpDispatchProxy
-{
-    [Post("https://oapi.dingtalk.com/attendance/listRecord"), JsonSerialization(typeof(NewtonsoftJsonSerializerProvider))]
-    Task<string> ListRecord([QueryString("access_token")] string accessToken, [Body] ListRecordRequest request);
-
-    [Post("https://api.dingtalk.com/v1.0/attendance/approvals/finish"), JsonSerialization(typeof(NewtonsoftJsonSerializerProvider))]
-    Task<string> ApprovalsFinish([QueryString] string userId, [Headers("x-acs-dingtalk-access-token")] string accessToken
-        , [Body] AttdendanceApprovalsFinishRequest request);
-
-    [Post("https://oapi.dingtalk.com/topapi/attendance/approve/cancel?access_token={accessToken}"), JsonSerialization(typeof(NewtonsoftJsonSerializerProvider))]
-    Task<string> ApprovalsCancel(string accessToken, [Body] AttendanceApprovelsCancelRequest request);
-
-    [Post("https://oapi.dingtalk.com/topapi/attendance/getleavestatus?access_token={accessToken}"), JsonSerialization(typeof(NewtonsoftJsonSerializerProvider))]
-    Task<string> GetLeaveStatus(string accessToken, [Body] GetLeaveStatusRequest request);
-}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно