夜鹰 7 месяцев назад
Родитель
Сommit
3d3517c011
30 измененных файлов с 828 добавлено и 121 удалено
  1. 16 16
      Admin.NET/Admin.NET.Application/Configuration/SMS.json
  2. 10 12
      Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
  3. 1 4
      Admin.NET/Admin.NET.Core/Entity/SysFileProvider.cs
  4. 5 5
      Admin.NET/Admin.NET.Core/Enum/ErrorCodeEnum.cs
  5. 2 2
      Admin.NET/Admin.NET.Core/Extension/SqlSugarExtension.cs
  6. 1 1
      Admin.NET/Admin.NET.Core/Service/File/Dto/FileProviderInput.cs
  7. 2 4
      Admin.NET/Admin.NET.Core/Service/File/FileProvider/MultiOSSFileProvider.cs
  8. 1 4
      Admin.NET/Admin.NET.Core/Service/File/IOSSServiceManager.cs
  9. 1 9
      Admin.NET/Admin.NET.Core/Service/File/SysFileProviderController.cs
  10. 3 7
      Admin.NET/Admin.NET.Core/Service/File/SysFileProviderService.cs
  11. 0 1
      Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs
  12. 23 1
      Admin.NET/Admin.NET.Core/Service/Role/SysRoleService.cs
  13. 1 3
      Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarRepository.cs
  14. 4 9
      Admin.NET/Admin.NET.Core/Utils/TenantHeaderOperationFilter.cs
  15. 2 2
      Admin.NET/Admin.NET.Test/Admin.NET.Test.csproj
  16. 1 1
      Admin.NET/Admin.NET.Web.Core/Admin.NET.Web.Core.csproj
  17. 22 21
      Admin.NET/Admin.NET.Web.Core/Startup.cs
  18. 47 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkDept.cs
  19. 23 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkWokerflowConfig.cs
  20. 86 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkWokerflowLog.cs
  21. 95 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Job/SyncDingTalkDeptJob.cs
  22. 98 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Job/SyncWokerflowLogJob.cs
  23. 64 7
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/DingTalkService.cs
  24. 31 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkDeptOutput.cs
  25. 74 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkGetProcessInstancesOutput.cs
  26. 105 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkWorkflowProcessInstancesInput.cs
  27. 27 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkWorkflowProcessInstancesOutput.cs
  28. 12 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkDeptInput.cs
  29. 70 11
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/IDingTalkApi.cs
  30. 1 1
      Admin.NET/Plugins/Admin.NET.Plugin.ReZero/Admin.NET.Plugin.ReZero.csproj

+ 16 - 16
Admin.NET/Admin.NET.Application/Configuration/SMS.json

@@ -39,22 +39,22 @@
     },
     // 自定义短信接口
     "Custom": {
-        "Enabled": false, // 是否启用自定义短信接口
-        "Method": "GET", // 请求方法: GET 或 POST
-        "ApiUrl": "https://api.xxxx.com/sms?u=xxxx&key=59e03f49c3dbb5033&m={mobile}&c={content}", // API接口地址,支持占位符: {mobile}, {content}, {code}
-        "ContentType": "application/x-www-form-urlencoded", // POST请求的Content-Type: application/json 或 application/x-www-form-urlencoded
-        "PostData": "", // POST请求的数据模板,支持占位符,JSON 格式示例: {"mobile":"{mobile}","content":"{content}","apikey":"your_key"};Form 格式示例: mobile={mobile}&content={content}&apikey=your_key
-        "SuccessFlag": "0", // 成功响应标识,响应内容包含此字符串则认为发送成功
-        "Templates": [
-            {
-                "Id": "0",
-                "Content": "您的验证码为:{code},请勿泄露于他人!"
-            },
-            {
-                "Id": "1",
-                "Content": "注册成功,感谢您的注册,请妥善保管您的账户信息"
-            }
-        ]
+      "Enabled": false, // 是否启用自定义短信接口
+      "Method": "GET", // 请求方法: GET 或 POST
+      "ApiUrl": "https://api.xxxx.com/sms?u=xxxx&key=59e03f49c3dbb5033&m={mobile}&c={content}", // API接口地址,支持占位符: {mobile}, {content}, {code}
+      "ContentType": "application/x-www-form-urlencoded", // POST请求的Content-Type: application/json 或 application/x-www-form-urlencoded
+      "PostData": "", // POST请求的数据模板,支持占位符,JSON 格式示例: {"mobile":"{mobile}","content":"{content}","apikey":"your_key"};Form 格式示例: mobile={mobile}&content={content}&apikey=your_key
+      "SuccessFlag": "0", // 成功响应标识,响应内容包含此字符串则认为发送成功
+      "Templates": [
+        {
+          "Id": "0",
+          "Content": "您的验证码为:{code},请勿泄露于他人!"
+        },
+        {
+          "Id": "1",
+          "Content": "注册成功,感谢您的注册,请妥善保管您的账户信息"
+        }
+      ]
     }
   }
 }

+ 10 - 12
Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj

@@ -15,13 +15,13 @@
   <ItemGroup>
     <PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="4.0.1" />
     <PackageReference Include="AlipaySDKNet.Standard" Version="4.9.874" />
-    <PackageReference Include="AngleSharp" Version="1.3.1" />
+    <PackageReference Include="AngleSharp" Version="1.4.0" />
     <PackageReference Include="AspectCore.Extensions.Reflection" Version="2.4.0" />
     <PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
     <PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.2.1" />
-    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.137" />
-    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.137" />
-    <PackageReference Include="Furion.Pure" Version="4.9.7.137" />
+    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.211" />
+    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.211" />
+    <PackageReference Include="Furion.Pure" Version="4.9.7.211" />
     <PackageReference Include="Hardware.Info" Version="101.1.0.1" />
     <PackageReference Include="Hashids.net" Version="1.7.0" />
     <PackageReference Include="IPTools.China" Version="1.6.0" />
@@ -32,9 +32,9 @@
     <PackageReference Include="Magicodes.IE.Pdf" Version="2.7.6" />
     <PackageReference Include="Magicodes.IE.Word" Version="2.7.6" />
     <PackageReference Include="MailKit" Version="4.14.1" />
-    <PackageReference Include="MiniExcel" Version="1.41.4" />
+    <PackageReference Include="MiniExcel" Version="1.42.0" />
     <PackageReference Include="MiniWord" Version="0.9.2" />
-    <PackageReference Include="NewLife.Redis" Version="6.3.2025.1001" />
+    <PackageReference Include="NewLife.Redis" Version="6.4.2025.1113" />
     <PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="4.0.0" />
     <PackageReference Include="OnceMi.AspNetCore.OSS" Version="1.2.0" />
     <PackageReference Include="QRCoder" Version="1.7.0" />
@@ -42,13 +42,11 @@
     <PackageReference Include="SixLabors.ImageSharp.Web" Version="3.2.0" />
     <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.12.0" />
     <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.16.0" />
-    <PackageReference Include="SqlSugar.MongoDbCore" Version="5.1.4.271" />
-    <PackageReference Include="SqlSugarCore" Version="5.1.4.207" />
+    <PackageReference Include="SqlSugar.MongoDbCore" Version="5.1.4.275" />
+    <PackageReference Include="SqlSugarCore" Version="5.1.4.210" />
     <PackageReference Include="SSH.NET" Version="2025.1.0" />
-    <PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.10" />
-    <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.1339" />
+    <PackageReference Include="System.Linq.Dynamic.Core" Version="1.7.0" />
+    <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1346" />
     <PackageReference Include="UAParser" Version="3.1.47" />
     <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
     <PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" Aliases="BouncyCastleV2" />

+ 1 - 4
Admin.NET/Admin.NET.Core/Entity/SysFileProvider.cs

@@ -5,7 +5,6 @@
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 using Newtonsoft.Json;
-using static Microsoft.ApplicationInsights.MetricDimensionNames.TelemetryContext;
 
 namespace Admin.NET.Core;
 
@@ -33,7 +32,6 @@ public partial class SysFileProvider : EntityBaseTenant
     [Required, MaxLength(32)]
     public virtual string BucketName { get; set; }
 
-
     /// <summary>
     /// 访问密钥 (填入 阿里云(Aliyun)/Minio:的 AccessKey,腾讯云(QCloud): 的 SecretId)
     /// </summary>
@@ -41,7 +39,6 @@ public partial class SysFileProvider : EntityBaseTenant
     [MaxLength(128)]
     public virtual string? AccessKey { get; set; }
 
-  
     /// <summary>
     /// 密钥
     /// </summary>
@@ -118,4 +115,4 @@ public partial class SysFileProvider : EntityBaseTenant
     /// </summary>
     [SugarColumn(IsIgnore = true)]
     public virtual string ConfigKey => $"{Provider}_{BucketName}_{Id}";
-}
+}

+ 5 - 5
Admin.NET/Admin.NET.Core/Enum/ErrorCodeEnum.cs

@@ -61,11 +61,11 @@ public enum ErrorCodeEnum
     [ErrorCodeItemMetadata("旧密码输入错误")]
     D1004,
 
-    /// <summary>
-    /// 测试数据禁止更改admin密码
-    /// </summary>
-    [ErrorCodeItemMetadata("测试数据禁止更改用户【admin】密码")]
-    D1005,
+    ///// <summary>
+    ///// 测试数据禁止更改admin密码
+    ///// </summary>
+    //[ErrorCodeItemMetadata("测试数据禁止更改用户【admin】密码")]
+    //D1005,
 
     /// <summary>
     /// 数据已存在

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

@@ -102,8 +102,8 @@ public static class SqlSugarExtension
     {
         return filterLogic switch
         {
-            FilterLogicEnum.And => Expression.And(bExpresionBase, bExpresion),
-            FilterLogicEnum.Or => Expression.Or(bExpresionBase, bExpresion),
+            FilterLogicEnum.And => Expression.AndAlso(bExpresionBase, bExpresion),
+            FilterLogicEnum.Or => Expression.OrElse(bExpresionBase, bExpresion),
             FilterLogicEnum.Xor => Expression.ExclusiveOr(bExpresionBase, bExpresion),
             _ => throw new ArgumentException("FilterLogic is not valid.", nameof(filterLogic)),
         };

+ 1 - 1
Admin.NET/Admin.NET.Core/Service/File/Dto/FileProviderInput.cs

@@ -179,4 +179,4 @@ public class SelectProviderInput
     /// 指定存储桶名称
     /// </summary>
     public string? BucketName { get; set; }
-}
+}

+ 2 - 4
Admin.NET/Admin.NET.Core/Service/File/FileProvider/MultiOSSFileProvider.cs

@@ -4,8 +4,6 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
-using OnceMi.AspNetCore.OSS;
-
 namespace Admin.NET.Core.Service;
 
 /// <summary>
@@ -204,7 +202,7 @@ public class MultiOSSFileProvider : ICustomFileProvider, ITransient
             "ALIYUN" => $"{protocol}://{bucketName}.{provider.Endpoint}/{filePath.TrimStart('/')}",
             "QCLOUD" => $"{protocol}://{bucketName}-{provider.Endpoint}.cos.{provider.Region}.myqcloud.com/{filePath.TrimStart('/')}",
             "MINIO" => $"{protocol}://{provider.Endpoint}/{bucketName}/{filePath.TrimStart('/')}",
-             _ => throw Oops.Oh($"不支持的OSS提供者: {provider.Provider}")
+            _ => throw Oops.Oh($"不支持的OSS提供者: {provider.Provider}")
         };
     }
 
@@ -254,4 +252,4 @@ public class MultiOSSFileProvider : ICustomFileProvider, ITransient
             return Task.FromResult<SysFileProvider?>(null);
         }
     }
-}
+}

+ 1 - 4
Admin.NET/Admin.NET.Core/Service/File/IOSSServiceManager.cs

@@ -1,7 +1,4 @@
 using OnceMi.AspNetCore.OSS;
-using System.Collections.Concurrent;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
 
 namespace Admin.NET.Core.Service;
 
@@ -211,4 +208,4 @@ public class OSSServiceManager : IOSSServiceManager, ITransient
             _disposed = true;
         }
     }
-}
+}

+ 1 - 9
Admin.NET/Admin.NET.Core/Service/File/SysFileProviderController.cs

@@ -87,10 +87,6 @@ public class SysFileProviderController : IDynamicApiController, ITransient
         await _fileProviderService.DeleteFileProvider(input);
     }
 
-
-
-
-
     /// <summary>
     /// 根据存储桶名称获取存储提供者 🔖
     /// </summary>
@@ -183,12 +179,8 @@ public class SysFileProviderController : IDynamicApiController, ITransient
     {
         return await _fileProviderService.GetBucketProviderMapping();
     }
-
-
 }
 
-
-
 /// <summary>
 /// 批量启用/禁用存储提供者输入参数
 /// </summary>
@@ -204,4 +196,4 @@ public class BatchEnableProviderInput
     /// 是否启用
     /// </summary>
     public bool IsEnable { get; set; }
-}
+}

+ 3 - 7
Admin.NET/Admin.NET.Core/Service/File/SysFileProviderService.cs

@@ -5,8 +5,6 @@
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 using OnceMi.AspNetCore.OSS;
-using Newtonsoft.Json;
-using Microsoft.Extensions.DependencyInjection;
 
 namespace Admin.NET.Core.Service;
 
@@ -43,7 +41,7 @@ public class SysFileProviderService : IDynamicApiController, ITransient
     /// <returns></returns>
     [DisplayName("获取文件存储提供者分页列表")]
     [NonAction]
-    public async Task<SqlSugarPagedList<SysFileProvider>> GetFileProviderPage(PageFileProviderInput input)
+    public async Task<SqlSugarPagedList<SysFileProvider>> GetFileProviderPage([FromQuery] PageFileProviderInput input)
     {
         return await _sysFileProviderRep.AsQueryable()
             .WhereIF(!string.IsNullOrWhiteSpace(input.Provider), u => u.Provider.Contains(input.Provider!))
@@ -351,7 +349,6 @@ public class SysFileProviderService : IDynamicApiController, ITransient
         await Task.CompletedTask;
     }
 
-
     /// <summary>
     /// 获取所有可用的存储桶列表
     /// </summary>
@@ -499,7 +496,7 @@ public class SysFileProviderService : IDynamicApiController, ITransient
         {
             // 确保只有一个默认提供者,将其他提供者的默认标识设为false
             await _sysFileProviderRep.AsUpdateable()
-                .SetColumns(p => p.IsDefault == false) 
+                .SetColumns(p => p.IsDefault == false)
                 .Where(p => p.IsDefault == true && p.Id != provider.Id)
                 .ExecuteCommandAsync();
         }
@@ -508,7 +505,6 @@ public class SysFileProviderService : IDynamicApiController, ITransient
         {
             provider.IsDefault ??= false;
         }
-           
 
         // 检查是否还有其他默认提供者,如果没有且当前提供者启用,则设为默认
         var hasDefaultProvider = await _sysFileProviderRep.AsQueryable()
@@ -521,4 +517,4 @@ public class SysFileProviderService : IDynamicApiController, ITransient
             provider.IsDefault = true;
         }
     }
-}
+}

+ 0 - 1
Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs

@@ -7,7 +7,6 @@
 using Aliyun.OSS.Util;
 using Furion.AspNetCore;
 using Microsoft.AspNetCore.Mvc.ModelBinding;
-using Microsoft.Extensions.Configuration;
 
 namespace Admin.NET.Core.Service;
 

+ 23 - 1
Admin.NET/Admin.NET.Core/Service/Role/SysRoleService.cs

@@ -19,6 +19,7 @@ public class SysRoleService : IDynamicApiController, ITransient
     private readonly SysRoleOrgService _sysRoleOrgService;
     private readonly SysMenuService _sysMenuService;
     private readonly SysOrgService _sysOrgService;
+    private readonly SysCacheService _sysCacheService;
 
     public SysRoleService(UserManager userManager,
         SysOrgService sysOrgService,
@@ -26,7 +27,8 @@ public class SysRoleService : IDynamicApiController, ITransient
         SysRoleOrgService sysRoleOrgService,
         SqlSugarRepository<SysRole> sysRoleRep,
         SysRoleMenuService sysRoleMenuService,
-        SysUserRoleService sysUserRoleService)
+        SysUserRoleService sysUserRoleService,
+        SysCacheService sysCacheService)
     {
         _userManager = userManager;
         _sysRoleRep = sysRoleRep;
@@ -35,6 +37,7 @@ public class SysRoleService : IDynamicApiController, ITransient
         _sysRoleOrgService = sysRoleOrgService;
         _sysRoleMenuService = sysRoleMenuService;
         _sysUserRoleService = sysUserRoleService;
+        _sysCacheService = sysCacheService;
     }
 
     /// <summary>
@@ -164,6 +167,10 @@ public class SysRoleService : IDynamicApiController, ITransient
     [DisplayName("授权角色菜单")]
     public async Task GrantMenu(RoleMenuInput input)
     {
+        if (input.MenuIdList == null || input.MenuIdList.Count < 1) return;
+
+        await ClearUserApiCache(input.Id);
+
         await _sysRoleMenuService.GrantRoleMenu(input);
     }
 
@@ -251,4 +258,19 @@ public class SysRoleService : IDynamicApiController, ITransient
             .Where(u => u.Id == input.Id)
             .ExecuteCommandAsync();
     }
+
+    /// <summary>
+    /// 删除与该角色相关的用户接口缓存
+    /// </summary>
+    /// <param name="roleId"></param>
+    /// <returns></returns>
+    [NonAction]
+    public async Task ClearUserApiCache(long roleId)
+    {
+        var userIdList = await _sysUserRoleService.GetUserIdList(roleId);
+        foreach (var userId in userIdList)
+        {
+            _sysCacheService.Remove($"{CacheConst.KeyUserButton}{userId}");
+        }
+    }
 }

+ 1 - 3
Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarRepository.cs

@@ -4,8 +4,6 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
-using NewLife.Reflection;
-
 namespace Admin.NET.Core;
 
 /// <summary>
@@ -39,7 +37,7 @@ public class SqlSugarRepository<T> : SimpleClient<T>, ISqlSugarRepository<T> whe
             return;
 
         // 看请求头有没有租户id
-        var tenantId = App.HttpContext?.Request.Headers.GetValue(ClaimConst.TenantId, false)?.ToString();
+        var tenantId = App.HttpContext?.Request.Headers[ClaimConst.TenantId].FirstOrDefault();
         if (tenantId == SqlSugarConst.MainConfigId) return;
         else if (string.IsNullOrWhiteSpace(tenantId))
         {

+ 4 - 9
Admin.NET/Admin.NET.Core/Utils/TenantHeaderOperationFilter.cs

@@ -1,11 +1,5 @@
-using Admin.NET.Core;
-using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi;
 using Swashbuckle.AspNetCore.SwaggerGen;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace Admin.NET.Core;
 
@@ -19,15 +13,16 @@ public class TenantHeaderOperationFilter : IOperationFilter
     /// </summary>
     public void Apply(OpenApiOperation operation, OperationFilterContext context)
     {
-        operation.Parameters ??= new List<OpenApiParameter>();
+        operation.Parameters ??= [];
 
         operation.Parameters.Add(new OpenApiParameter
         {
             Name = ClaimConst.TenantId,
             In = ParameterLocation.Header,
+            Schema = new OpenApiSchema { Type = JsonSchemaType.String },
             Required = false,
             AllowEmptyValue = true,
             Description = "租户ID(留空表示默认租户)"
         });
     }
-}
+}

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

@@ -12,8 +12,8 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Furion.Xunit" Version="4.9.7.137" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
+    <PackageReference Include="Furion.Xunit" Version="4.9.7.211" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
     <PackageReference Include="Selenium.Support" Version="4.38.0" />
     <PackageReference Include="Selenium.WebDriver" Version="4.38.0" />
     <PackageReference Include="Selenium.WebDriver.MSEdgeDriver" Version="141.0.3537.71" />

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

@@ -11,7 +11,7 @@
 
   <ItemGroup>
     <PackageReference Include="IGeekFan.AspNetCore.Knife4jUI" Version="0.0.16" />
-    <PackageReference Include="System.Security.Cryptography.Pkcs" Version="9.0.10" />
+    <PackageReference Include="System.Security.Cryptography.Pkcs" Version="10.0.0" />
   </ItemGroup>
 
   <ItemGroup>

+ 22 - 21
Admin.NET/Admin.NET.Web.Core/Startup.cs

@@ -221,25 +221,25 @@ public class Startup : AppStartup
         // 控制台logo
         services.AddConsoleLogo();
 
-        // Swagger 时间格式化
-        services.AddSwaggerGen(c =>
-        {
-            c.MapType<DateTime>(() => new Microsoft.OpenApi.Models.OpenApiSchema
-            {
-                Type = "string",
-                Format = "date-time",
-                Example = new Microsoft.OpenApi.Any.OpenApiString(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")) // 示例值
-            });
-
-            // 确保生成的文档包含 OpenAPI 版本字段
-            c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
-            {
-                Version = "v1",
-                Title = "Admin.NET API",
-                Description = "Admin.NET 通用权限开发平台"
-            });
-            c.OperationFilter<TenantHeaderOperationFilter>();
-        });
+        //// Swagger 时间格式化
+        //services.AddSwaggerGen(c =>
+        //{
+        //    c.MapType<DateTime>(() => new Microsoft.OpenApi.Models.OpenApiSchema
+        //    {
+        //        Type = "string",
+        //        Format = "date-time",
+        //        Example = new Microsoft.OpenApi.Any.OpenApiString(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")) // 示例值
+        //    });
+
+        //    // 确保生成的文档包含 OpenAPI 版本字段
+        //    c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
+        //    {
+        //        Version = "v1",
+        //        Title = "Admin.NET API",
+        //        Description = "Admin.NET 通用权限开发平台"
+        //    });
+        //    c.OperationFilter<TenantHeaderOperationFilter>();
+        //});
 
         // 将IP地址数据库文件完全加载到内存,提升查询速度(以空间换时间,内存将会增加60-70M)
         IpToolSettings.LoadInternationalDbToMemory = true;
@@ -379,14 +379,15 @@ public class Startup : AppStartup
             {
                 groupInfo.Description += "<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>";
             }
-            options.ConfigureSwagger(m => {
+            options.ConfigureSwagger(m =>
+            {
                 m.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0;
             });
         });
 
 #if NET9_0_OR_GREATER
         app.UseAutoVersionUpdate();
-#endif 
+#endif
 
         app.UseEndpoints(endpoints =>
         {

+ 47 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkDept.cs

@@ -0,0 +1,47 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.DingTalk;
+
+/// <summary>
+/// 钉钉部门信息
+/// </summary>
+[SugarTable("ding_talk_dept", "钉钉部门表")]
+public class DingTalkDept
+{
+    /// <summary>
+    /// 部门id
+    /// </summary>
+    [SugarColumn(ColumnName = "Id", ColumnDescription = "部门id", IsPrimaryKey = true, IsIdentity = false)]
+    [Required]
+    public long dept_id { get; set; }
+
+    /// <summary>
+    /// 上级部门id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "上级部门id")]
+    [Required]
+    public virtual long parent_id { get; set; }
+
+    /// <summary>
+    /// 部门名
+    /// </summary>
+    [SugarColumn(ColumnDescription = "部门名", Length = 64)]
+    [MaxLength(64)]
+    public string? name { get; set; }
+
+    /// <summary>
+    /// 创建时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "创建时间", IsNullable = true, IsOnlyIgnoreUpdate = true)]
+    public virtual DateTime CreateTime { get; set; }
+
+    /// <summary>
+    /// 更新时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "更新时间")]
+    public virtual DateTime? UpdateTime { get; set; }
+}

+ 23 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkWokerflowConfig.cs

@@ -0,0 +1,23 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.DingTalk;
+
+[SugarTable("ding_talk_wokerflow_config", "审批配置表")]
+public class DingTalkWokerflowConfig
+{
+    /// <summary>
+    /// 审批名
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审批名", IsPrimaryKey = true, IsIdentity = false)]
+    public string WorkflowName { get; set; }
+
+    /// <summary>
+    /// 审批单Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审批单Id")]
+    public string ProcessCode { get; set; }
+}

+ 86 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkWokerflowLog.cs

@@ -0,0 +1,86 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.DingTalk;
+
+[SugarTable("ding_talk_wokerflow_log", "钉钉审批日志")]
+public class DingTalkWokerflowLog
+{
+    /// <summary>
+    /// 审批实例ID
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审批实例ID", IsPrimaryKey = true, IsIdentity = false)]
+    public string instanceId { get; set; }
+
+    /// <summary>
+    /// 审批单号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审批单号")]
+    public string? WorkflowId { get; set; }
+
+    /// <summary>
+    /// 来源单据
+    /// </summary>
+    [SugarColumn(ColumnDescription = "来源单据")]
+    public string SourceDocument { get; set; }
+
+    /// <summary>
+    /// 审批完成时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审批完成时间")]
+    public DateTime? EndTime { get; set; }
+
+    /// <summary>
+    /// 其他信息
+    /// </summary>
+    [SugarColumn(ColumnDescription = "其他信息", IsJson = true)]
+    public Dictionary<string, object>? other_info { get; set; }
+
+    /// <summary>
+    /// 是否回传结果给第三方
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否回传结果")]
+    public bool? isReturn { get; set; }
+
+    /// <summary>
+    /// 审批状态
+    /// </summary>
+    /// <remarks>
+    /// RUNNING:审批中 TERMINATED:已撤销 COMPLETED:审批完成
+    /// /// </remarks>
+    [SugarColumn(ColumnDescription = "审批状态")]
+    public string Status { get; set; }
+
+    /// <summary>
+    /// 任务ID
+    /// </summary>
+    [SugarColumn(ColumnDescription = "任务ID")]
+    public long? taskId { get; set; }
+
+    /// <summary>
+    /// 审批结果 agree:同意 refuse:拒绝
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审批结果")]
+    public string? Result { get; set; }
+
+    /// <summary>
+    /// 创建者姓名
+    /// </summary>
+    [SugarColumn(ColumnDescription = "创建者姓名", Length = 64, IsOnlyIgnoreUpdate = true)]
+    public string? CreateUserName { get; set; }
+
+    /// <summary>
+    /// 创建时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "创建时间", IsNullable = true, IsOnlyIgnoreUpdate = true)]
+    public DateTime CreateTime { get; set; }
+
+    /// <summary>
+    /// 更新时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "更新时间")]
+    public virtual DateTime? UpdateTime { get; set; }
+}

+ 95 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Job/SyncDingTalkDeptJob.cs

@@ -0,0 +1,95 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Admin.NET.Plugin.DingTalk;
+using Furion.Schedule;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace Admin.NET.Plugin.Job;
+
+/// <summary>
+/// 同步钉钉角色job,自动同步触发器请在web页面按需求设置
+/// </summary>
+[JobDetail("SyncDingTalkDeptJob", Description = "同步钉钉部门", GroupName = "default", Concurrent = false)]
+[Daily(TriggerId = "SyncDingTalkDeptTrigger", Description = "同步钉钉部门")]
+public class SyncDingTalkDeptJob : IJob
+{
+    private readonly IServiceScopeFactory _scopeFactory;
+    private readonly IDingTalkApi _dingTalkApi;
+    private readonly ILogger _logger;
+    private readonly SqlSugarRepository<DingTalkDept> _dingTalkDeptRep;
+
+    public SyncDingTalkDeptJob(
+        IServiceScopeFactory scopeFactory,
+        IDingTalkApi dingTalkApi,
+        SqlSugarRepository<DingTalkDept> dingTalkDeptRep,
+        ILoggerFactory loggerFactory)
+    {
+        _scopeFactory = scopeFactory;
+        _dingTalkApi = dingTalkApi;
+        _dingTalkDeptRep = dingTalkDeptRep;
+        _logger = loggerFactory.CreateLogger(CommonConst.SysLogCategoryName);
+    }
+
+    public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
+    {
+        using var serviceScope = _scopeFactory.CreateScope();
+        var _dingTalkOptions = serviceScope.ServiceProvider.GetRequiredService<IOptions<DingTalkOptions>>();
+
+        // 获取Token
+        var tokenRes = await _dingTalkApi.GetDingTalkToken(_dingTalkOptions.Value.ClientId, _dingTalkOptions.Value.ClientSecret);
+        if (tokenRes.ErrCode != 0)
+            throw Oops.Oh(tokenRes.ErrMsg);
+
+        var dingTalkDeptList = new List<DingTalkDept>();
+        // 获取部门列表
+        var deptIdsRes = await _dingTalkApi.GetDingTalkDept(tokenRes.AccessToken, new GetDingTalkDeptInput
+        { dept_id = 1 });
+        if (deptIdsRes.ErrCode != 0)
+        {
+            _logger.LogError(deptIdsRes.ErrMsg);
+            throw Oops.Oh(deptIdsRes.ErrMsg);
+        }
+        dingTalkDeptList.AddRange(deptIdsRes.Result.Select(d => new DingTalkDept
+        {
+            dept_id = d.dept_id,
+            name = d.name,
+            parent_id = d.parent_id
+        }));
+        foreach (var item in deptIdsRes.Result)
+        {
+            dingTalkDeptList.AddRange(await GetDingTalkDeptList(tokenRes.AccessToken, item.dept_id));
+        }
+        await _dingTalkDeptRep.InsertOrUpdateAsync(dingTalkDeptList);
+        var originColor = Console.ForegroundColor;
+        Console.ForegroundColor = ConsoleColor.Blue;
+        Console.WriteLine("【" + DateTime.Now + "】同步钉钉部门");
+        Console.ForegroundColor = originColor;
+    }
+
+    private async Task<List<DingTalkDept>> GetDingTalkDeptList(string token, long dept_id)
+    {
+        List<DingTalkDept> listTemp = new List<DingTalkDept>();
+        var deptIdsRes = await _dingTalkApi.GetDingTalkDept(token, new GetDingTalkDeptInput
+        { dept_id = dept_id });
+        if (deptIdsRes.ErrCode != 0)
+        {
+            return null;
+        }
+        listTemp.AddRange(deptIdsRes.Result.Select(x => new DingTalkDept
+        {
+            dept_id = x.dept_id,
+            name = x.name,
+            parent_id = x.parent_id
+        }));
+        foreach (var item in deptIdsRes.Result)
+        {
+            listTemp.AddRange(await GetDingTalkDeptList(token, item.dept_id));
+        }
+        return listTemp;
+    }
+}

+ 98 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Job/SyncWokerflowLogJob.cs

@@ -0,0 +1,98 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Admin.NET.Plugin.DingTalk;
+using Furion.Schedule;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace Admin.NET.Plugin.Job;
+
+/// <summary>
+/// 同步钉钉角色job,自动同步触发器请在web页面按需求设置
+/// </summary>
+[JobDetail(
+    "SyncWokerflowLogJob",
+    Description = "同步钉钉审批状态",
+    GroupName = "default",
+    Concurrent = false
+)]
+[Daily(TriggerId = "SyncWokerflowLogTrigger", Description = "同步钉钉审批状态")]
+public class SyncWokerflowLogJob : IJob
+{
+    private readonly IServiceScopeFactory _scopeFactory;
+    private readonly IDingTalkApi _dingTalkApi;
+    private readonly ILogger _logger;
+    private readonly SqlSugarRepository<DingTalkDept> _dingTalkDeptRep;
+    private readonly SqlSugarRepository<DingTalkWokerflowLog> _dingTalkWokerflowLogRep;
+
+    public SyncWokerflowLogJob(
+        IServiceScopeFactory scopeFactory,
+        IDingTalkApi dingTalkApi,
+        SqlSugarRepository<DingTalkDept> dingTalkDeptRep,
+        SqlSugarRepository<DingTalkWokerflowLog> dingTalkWokerflowLogRep,
+        ILoggerFactory loggerFactory
+    )
+    {
+        _scopeFactory = scopeFactory;
+        _dingTalkApi = dingTalkApi;
+        _dingTalkDeptRep = dingTalkDeptRep;
+        _dingTalkWokerflowLogRep = dingTalkWokerflowLogRep;
+        _logger = loggerFactory.CreateLogger(CommonConst.SysLogCategoryName);
+    }
+
+    public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
+    {
+        using var serviceScope = _scopeFactory.CreateScope();
+        var _dingTalkOptions = serviceScope.ServiceProvider.GetRequiredService<
+            IOptions<DingTalkOptions>
+        >();
+
+        // 获取Token
+        var tokenRes = await _dingTalkApi.GetDingTalkToken(
+            _dingTalkOptions.Value.ClientId,
+            _dingTalkOptions.Value.ClientSecret
+        );
+        if (tokenRes.ErrCode != 0)
+            throw Oops.Oh(tokenRes.ErrMsg);
+
+        var dingTalkDeptList = new List<DingTalkDept>();
+        // 获取未完成审批列表
+        List<DingTalkWokerflowLog> flow_list = await _dingTalkWokerflowLogRep.GetListAsync(t =>
+            t.Status == "RUNNING"
+        );
+        List<DingTalkWokerflowLog> update_list = new List<DingTalkWokerflowLog>();
+        if (flow_list?.Count > 0)
+        {
+            foreach (var item in flow_list)
+            {
+                var flow = await _dingTalkApi.GetProcessInstances(
+                    tokenRes.AccessToken,
+                    item.instanceId
+                );
+                if (flow.Result.Status != item.Status)
+                {
+                    item.Status = flow.Result.Status;
+                    item.UpdateTime = DateTime.Now;
+                    item.WorkflowId = flow.Result.BusinessId;
+                    item.taskId = flow
+                        .Result.Tasks.FirstOrDefault(t => t.Status == "RUNNING")
+                        ?.TaskId;
+                    update_list.Add(item);
+                }
+            }
+
+            if (update_list.Count > 0)
+            {
+                await _dingTalkWokerflowLogRep.UpdateRangeAsync(update_list);
+            }
+            var originColor = Console.ForegroundColor;
+            Console.ForegroundColor = ConsoleColor.Blue;
+            Console.WriteLine("【" + DateTime.Now + "】同步钉钉审批记录状态");
+            Console.ForegroundColor = originColor;
+        }
+    }
+}

+ 64 - 7
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/DingTalkService.cs

@@ -14,12 +14,17 @@ public class DingTalkService : IDynamicApiController, IScoped
 {
     private readonly IDingTalkApi _dingTalkApi;
     private readonly DingTalkOptions _dingTalkOptions;
+    private readonly SqlSugarRepository<DingTalkWokerflowLog> _dingTalkWokerflowLogRep;
 
-    public DingTalkService(IDingTalkApi dingTalkApi,
-        IOptions<DingTalkOptions> dingTalkOptions)
+    public DingTalkService(
+        IDingTalkApi dingTalkApi,
+        IOptions<DingTalkOptions> dingTalkOptions,
+        SqlSugarRepository<DingTalkWokerflowLog> dingTalkWokerflowLogRep
+    )
     {
         _dingTalkApi = dingTalkApi;
         _dingTalkOptions = dingTalkOptions.Value;
+        _dingTalkWokerflowLogRep = dingTalkWokerflowLogRep;
     }
 
     /// <summary>
@@ -29,7 +34,10 @@ public class DingTalkService : IDynamicApiController, IScoped
     [DisplayName("获取企业内部应用的access_token")]
     public async Task<GetDingTalkTokenOutput> GetDingTalkToken()
     {
-        var tokenRes = await _dingTalkApi.GetDingTalkToken(_dingTalkOptions.ClientId, _dingTalkOptions.ClientSecret);
+        var tokenRes = await _dingTalkApi.GetDingTalkToken(
+            _dingTalkOptions.ClientId,
+            _dingTalkOptions.ClientSecret
+        );
         if (tokenRes.ErrCode != 0)
         {
             throw Oops.Oh(tokenRes.ErrMsg);
@@ -44,7 +52,12 @@ public class DingTalkService : IDynamicApiController, IScoped
     /// <param name="input"></param>
     /// <returns></returns>
     [HttpPost, DisplayName("获取在职员工列表")]
-    public async Task<DingTalkBaseResponse<GetDingTalkCurrentEmployeesListOutput>> GetDingTalkCurrentEmployeesList(string access_token, [Required] GetDingTalkCurrentEmployeesListInput input)
+    public async Task<
+        DingTalkBaseResponse<GetDingTalkCurrentEmployeesListOutput>
+    > GetDingTalkCurrentEmployeesList(
+        string access_token,
+        [Required] GetDingTalkCurrentEmployeesListInput input
+    )
     {
         return await _dingTalkApi.GetDingTalkCurrentEmployeesList(access_token, input);
     }
@@ -56,7 +69,12 @@ public class DingTalkService : IDynamicApiController, IScoped
     /// <param name="input"></param>
     /// <returns></returns>
     [HttpPost, DisplayName("获取员工花名册字段信息")]
-    public async Task<DingTalkBaseResponse<List<DingTalkEmpRosterFieldVo>>> GetDingTalkCurrentEmployeesRosterList(string access_token, [Required] GetDingTalkCurrentEmployeesRosterListInput input)
+    public async Task<
+        DingTalkBaseResponse<List<DingTalkEmpRosterFieldVo>>
+    > GetDingTalkCurrentEmployeesRosterList(
+        string access_token,
+        [Required] GetDingTalkCurrentEmployeesRosterListInput input
+    )
     {
         return await _dingTalkApi.GetDingTalkCurrentEmployeesRosterList(access_token, input);
     }
@@ -68,7 +86,10 @@ public class DingTalkService : IDynamicApiController, IScoped
     /// <param name="input"></param>
     /// <returns></returns>
     [DisplayName("给指定用户发送钉钉互动卡片")]
-    public async Task<DingTalkSendInteractiveCardsOutput> DingTalkSendInteractiveCards(string token, DingTalkSendInteractiveCardsInput input)
+    public async Task<DingTalkSendInteractiveCardsOutput> DingTalkSendInteractiveCards(
+        string token,
+        DingTalkSendInteractiveCardsInput input
+    )
     {
         return await _dingTalkApi.DingTalkSendInteractiveCards(token, input);
     }
@@ -80,8 +101,44 @@ public class DingTalkService : IDynamicApiController, IScoped
     /// <param name="input"></param>
     /// <returns></returns>
     [DisplayName("给指定用户发送钉钉消息卡片")]
-    public async Task<DingTalkCreateAndDeliverOutput> DingTalkCreateAndDeliver(string token, DingTalkCreateAndDeliverInput input)
+    public async Task<DingTalkCreateAndDeliverOutput> DingTalkCreateAndDeliver(
+        string token,
+        DingTalkCreateAndDeliverInput input
+    )
     {
         return await _dingTalkApi.DingTalkCreateAndDeliver(token, input);
     }
+
+    [DisplayName("用于发起OA审批实例")]
+    public async Task<DingTalkWorkflowProcessInstancesOutput> DingTalkWorkflowProcessInstances(
+        string token,
+        DingTalkWorkflowProcessInstancesInput input
+    )
+    {
+        var temp = await _dingTalkApi.DingTalkWorkflowProcessInstances(token, input);
+        return temp;
+    }
+
+    [DisplayName("查询审批实例")]
+    public async Task<DingTalkGetProcessInstancesOutput> DingTalkWorkflowProcessInstances(
+        string token,
+        string input
+    )
+    {
+        var temp = await _dingTalkApi.GetProcessInstances(token, input);
+        DingTalkWokerflowLog flow = await _dingTalkWokerflowLogRep.GetFirstAsync(t =>
+            t.Status == "RUNNING" && t.instanceId == input
+        );
+
+        if ((flow != null) && (temp.Result.Status != flow.Status))
+        {
+            flow.Status = temp.Result.Status;
+            flow.UpdateTime = DateTime.Now;
+            flow.WorkflowId = temp.Result.BusinessId;
+            flow.Result = temp.Result.Result;
+            flow.taskId = temp.Result.Tasks.FirstOrDefault(t => t.Status == "RUNNING")?.TaskId;
+            await _dingTalkWokerflowLogRep.UpdateAsync(flow);
+        }
+        return temp;
+    }
 }

+ 31 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkDeptOutput.cs

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

+ 74 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkGetProcessInstancesOutput.cs

@@ -0,0 +1,74 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.DingTalk;
+
+public class DingTalkGetProcessInstancesOutput
+{
+    public ResultData Result { get; set; }
+    public bool Success { get; set; }
+}
+
+public class OperationRecord
+{
+    public DateTime? Date { get; set; }
+    public string Result { get; set; }
+    public List<object> Images { get; set; } // 图片可能是字符串 URL 或对象
+    public string ShowName { get; set; }
+    public string Type { get; set; }
+    public string UserId { get; set; }
+}
+
+// 表格行中的子项(用于 TableField 的解析)
+public class TableRowItem
+{
+    public string BizAlias { get; set; }
+    public string Label { get; set; }
+    public string Value { get; set; }
+    public string Key { get; set; }
+    public bool Mask { get; set; }
+}
+
+// 完整的一行表格数据
+public class TableRow
+{
+    public List<TableRowItem> RowValue { get; set; }
+    public string RowNumber { get; set; }
+}
+
+public class TaskItem
+{
+    public string Result { get; set; }
+    public string ActivityId { get; set; }
+    public string PcUrl { get; set; }
+    public DateTime? CreateTime { get; set; }
+    public string MobileUrl { get; set; }
+    public string UserId { get; set; }
+    public long TaskId { get; set; }
+    public string Status { get; set; }
+}
+
+public class ResultData
+{
+    public List<string> AttachedProcessInstanceIds { get; set; }
+    public string BusinessId { get; set; }
+    public string Title { get; set; }
+    public string OriginatorDeptId { get; set; }
+    public List<OperationRecord> OperationRecords { get; set; }
+    public List<FormComponentValue> FormComponentValues { get; set; }
+
+    /// <summary>
+    /// 审批结果 agree:同意 refuse:拒绝
+    /// </summary>
+    public string Result { get; set; }
+
+    public string BizAction { get; set; }
+    public DateTime? CreateTime { get; set; }
+    public string OriginatorUserId { get; set; }
+    public List<TaskItem> Tasks { get; set; }
+    public string OriginatorDeptName { get; set; }
+    public string Status { get; set; }
+}

+ 105 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkWorkflowProcessInstancesInput.cs

@@ -0,0 +1,105 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.DingTalk;
+
+public class DingTalkWorkflowProcessInstancesInput
+{
+    /// <summary>
+    /// 发起人用户ID
+    /// </summary>
+    public string OriginatorUserId { get; set; }
+
+    /// <summary>
+    /// 审批模板的流程编码
+    /// </summary>
+    public string ProcessCode { get; set; }
+
+    /// <summary>
+    /// 部门ID
+    /// </summary>
+    public long DeptId { get; set; }
+
+    /// <summary>
+    /// 微应用AgentId
+    /// </summary>
+    public long MicroappAgentId { get; set; }
+
+    /// <summary>
+    /// 审批人列表(支持多节点)
+    /// </summary>
+    public List<Approver> Approvers { get; set; }
+
+    /// <summary>
+    /// 抄送人列表
+    /// </summary>
+    public List<string> CcList { get; set; }
+
+    /// <summary>
+    /// 抄送位置:START(开始),MIDDLE(中间),END(结束)
+    /// </summary>
+    public string CcPosition { get; set; }
+
+    /// <summary>
+    /// 目标动态选择办理人(用于会签或或签等场景)
+    /// </summary>
+    public List<TargetSelectActioner> TargetSelectActioners { get; set; }
+
+    /// <summary>
+    /// 表单组件值列表
+    /// </summary>
+    public List<FormComponentValue> FormComponentValues { get; set; }
+
+    /// <summary>
+    /// 请求ID,用于幂等控制
+    /// </summary>
+    public string RequestId { get; set; }
+}
+
+/// <summary>
+/// 审批人信息
+/// </summary>
+public class Approver
+{
+    /// <summary>
+    /// 节点类型:AGREE(同意),REFUSE(拒绝)等
+    /// </summary>
+    public string ActionType { get; set; }
+
+    /// <summary>
+    /// 该节点的审批人用户ID列表
+    /// </summary>
+    public List<string> UserIds { get; set; }
+}
+
+/// <summary>
+/// 动态选择办理人
+/// </summary>
+public class TargetSelectActioner
+{
+    /// <summary>
+    /// 办理人Key,对应表单中的人员选择控件的key
+    /// </summary>
+    public string ActionerKey { get; set; }
+
+    /// <summary>
+    /// 该控件选中的用户ID列表
+    /// </summary>
+    public List<string> ActionerUserIds { get; set; }
+}
+
+/// <summary>
+/// 表单组件值
+/// </summary>
+public class FormComponentValue
+{
+    public string ComponentType { get; set; }
+    public string Name { get; set; }
+    public string BizAlias { get; set; }
+    public string Id { get; set; }
+    public string Value { get; set; }
+    public string ExtValue { get; set; }
+}

+ 27 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkWorkflowProcessInstancesOutput.cs

@@ -0,0 +1,27 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Plugin.DingTalk;
+
+public class DingTalkWorkflowProcessInstancesOutput
+{
+    /// <summary>
+    /// 请求Id
+    /// </summary>
+    [Newtonsoft.Json.JsonProperty("request_id")]
+    [System.Text.Json.Serialization.JsonPropertyName("request_id")]
+    public string RequestId { get; set; }
+
+    public string code { get; set; }
+    public string message { get; set; }
+
+    /// <summary>
+    /// 是否还有更多数据
+    /// </summary>
+    [JsonProperty("instanceId")]
+    [System.Text.Json.Serialization.JsonPropertyName("instanceId")]
+    public string instanceId { get; set; }
+}

+ 12 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkDeptInput.cs

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

+ 70 - 11
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/IDingTalkApi.cs

@@ -24,8 +24,13 @@ public interface IDingTalkApi : IHttpDeclarative
     /// <param name="input"></param>
     /// <returns></returns>
     [Post("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/queryonjob")]
-    Task<DingTalkBaseResponse<GetDingTalkCurrentEmployeesListOutput>> GetDingTalkCurrentEmployeesList([Query] string access_token,
-        [Body(ContentType = "application/json", UseStringContent = true), Required] GetDingTalkCurrentEmployeesListInput input);
+    Task<
+        DingTalkBaseResponse<GetDingTalkCurrentEmployeesListOutput>
+    > GetDingTalkCurrentEmployeesList(
+        [Query] string access_token,
+        [Body(ContentType = "application/json", UseStringContent = true), Required]
+            GetDingTalkCurrentEmployeesListInput input
+    );
 
     /// <summary>
     /// 获取员工花名册字段信息
@@ -34,8 +39,13 @@ public interface IDingTalkApi : IHttpDeclarative
     /// <param name="input"></param>
     /// <returns></returns>
     [Post("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/v2/list")]
-    Task<DingTalkBaseResponse<List<DingTalkEmpRosterFieldVo>>> GetDingTalkCurrentEmployeesRosterList([Query] string access_token,
-        [Body(ContentType = "application/json", UseStringContent = true), Required] GetDingTalkCurrentEmployeesRosterListInput input);
+    Task<
+        DingTalkBaseResponse<List<DingTalkEmpRosterFieldVo>>
+    > GetDingTalkCurrentEmployeesRosterList(
+        [Query] string access_token,
+        [Body(ContentType = "application/json", UseStringContent = true), Required]
+            GetDingTalkCurrentEmployeesRosterListInput input
+    );
 
     /// <summary>
     /// 发送钉钉互动卡片
@@ -51,7 +61,9 @@ public interface IDingTalkApi : IHttpDeclarative
     [Obsolete]
     Task<DingTalkSendInteractiveCardsOutput> DingTalkSendInteractiveCards(
         [Header("x-acs-dingtalk-access-token")] string token,
-        [Body(ContentType = "application/json", UseStringContent = true)] DingTalkSendInteractiveCardsInput input);
+        [Body(ContentType = "application/json", UseStringContent = true)]
+            DingTalkSendInteractiveCardsInput input
+    );
 
     /// <summary>
     /// 获取钉钉卡片消息读取状态
@@ -62,7 +74,8 @@ public interface IDingTalkApi : IHttpDeclarative
     [Get("https://api.dingtalk.com/v1.0/robot/oToMessages/readStatus")]
     Task<GetDingTalkCardMessageReadStatusOutput> GetDingTalkCardMessageReadStatus(
         [Header("x-acs-dingtalk-access-token")] string token,
-        [Query] GetDingTalkCardMessageReadStatusInput input);
+        [Query] GetDingTalkCardMessageReadStatusInput input
+    );
 
     /// <summary>
     /// 获取角色列表
@@ -71,8 +84,11 @@ public interface IDingTalkApi : IHttpDeclarative
     /// <param name="input"></param>
     /// <returns></returns>
     [Post("https://oapi.dingtalk.com/topapi/role/list")]
-    Task<DingTalkBaseResponse<DingTalkRoleListOutput>> GetDingTalkRoleList([Query] string access_token,
-        [Body(ContentType = "application/json", UseStringContent = true), Required] GetDingTalkCurrentRoleListInput input);
+    Task<DingTalkBaseResponse<DingTalkRoleListOutput>> GetDingTalkRoleList(
+        [Query] string access_token,
+        [Body(ContentType = "application/json", UseStringContent = true), Required]
+            GetDingTalkCurrentRoleListInput input
+    );
 
     /// <summary>
     /// 获取指定角色的员工列表
@@ -81,8 +97,11 @@ public interface IDingTalkApi : IHttpDeclarative
     /// <param name="input"></param>
     /// <returns></returns>
     [Post("https://oapi.dingtalk.com/topapi/role/simplelist")]
-    Task<DingTalkBaseResponse<DingTalkRoleSimplelistOutput>> GetDingTalkRoleSimplelist([Query] string access_token,
-        [Body(ContentType = "application/json", UseStringContent = true), Required] GetDingTalkCurrentRoleSimplelistInput input);
+    Task<DingTalkBaseResponse<DingTalkRoleSimplelistOutput>> GetDingTalkRoleSimplelist(
+        [Query] string access_token,
+        [Body(ContentType = "application/json", UseStringContent = true), Required]
+            GetDingTalkCurrentRoleSimplelistInput input
+    );
 
     /// <summary>
     /// 创建并投放钉钉消息卡片
@@ -93,5 +112,45 @@ public interface IDingTalkApi : IHttpDeclarative
     [Post("https://api.dingtalk.com/v1.0/card/instances/createAndDeliver")]
     Task<DingTalkCreateAndDeliverOutput> DingTalkCreateAndDeliver(
         [Header("x-acs-dingtalk-access-token")] string token,
-        [Body(ContentType = "application/json", UseStringContent = true)] DingTalkCreateAndDeliverInput input);
+        [Body(ContentType = "application/json", UseStringContent = true)]
+            DingTalkCreateAndDeliverInput input
+    );
+
+    /// <summary>
+    /// 获取部门列表列表
+    /// </summary>
+    /// <param name="access_token">调用该接口的应用凭证</param>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [Post("https://oapi.dingtalk.com/topapi/v2/department/listsub")]
+    Task<DingTalkBaseResponse<List<DingTalkDeptOutput>>> GetDingTalkDept(
+        [Query] string access_token,
+        [Body(ContentType = "application/json", UseStringContent = true), Required]
+            GetDingTalkDeptInput input
+    );
+
+    /// <summary>
+    /// 发起审批实例
+    /// </summary>
+    /// <param name="token"></param>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [Post("https://api.dingtalk.com/v1.0/workflow/processInstances")]
+    Task<DingTalkWorkflowProcessInstancesOutput> DingTalkWorkflowProcessInstances(
+        [Header("x-acs-dingtalk-access-token")] string token,
+        [Body(ContentType = "application/json", UseStringContent = true), Required]
+            DingTalkWorkflowProcessInstancesInput input
+    );
+
+    /// <summary>
+    /// 查询审批实例
+    /// </summary>
+    /// <param name="token"></param>
+    /// <param name="processInstanceId"></param>
+    /// <returns></returns>
+    [Get("https://api.dingtalk.com/v1.0/workflow/processInstances")]
+    Task<DingTalkGetProcessInstancesOutput> GetProcessInstances(
+        [Header("x-acs-dingtalk-access-token")] string token,
+        [Query] string processInstanceId
+    );
 }

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

@@ -25,7 +25,7 @@
 
   <ItemGroup>
     <PackageReference Include="ClosedXML" Version="0.105.0" />
-    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.14.0" />
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="5.0.0" />
     <PackageReference Include="Rezero.Api" Version="1.8.30" />
   </ItemGroup>