Ver código fonte

😎增加钉钉对接插件

zuohuaijun 2 anos atrás
pai
commit
76ee807260
29 arquivos alterados com 472 adições e 427 exclusões
  1. 2 0
      Admin.NET/Admin.NET.Application/Admin.NET.Application.csproj
  2. 1 1
      Admin.NET/Admin.NET.Core/Const/SqlSugarConst.cs
  3. 0 269
      Admin.NET/Admin.NET.Core/DingTalk/DingTalkService.cs
  4. 1 2
      Admin.NET/Admin.NET.Core/GlobalUsings.cs
  5. 0 1
      Admin.NET/Admin.NET.Web.Core/ProjectOptions.cs
  6. 7 0
      Admin.NET/Admin.NET.sln
  7. 28 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Admin.NET.Plugin.DingTalk.csproj
  8. 11 3
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Const/DingTalkConst.cs
  9. 3 5
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/DingTalk.json
  10. 15 17
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkUser.cs
  11. 5 3
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Enum/DingTalkConversationTypeEnum.cs
  12. 23 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/GlobalUsings.cs
  13. 4 1
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Option/DingTalkOptions.cs
  14. 209 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/DingTalkService.cs
  15. 9 13
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkBaseResponse.cs
  16. 4 4
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkCardData.cs
  17. 15 8
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkEmpFieldDataVo.cs
  18. 11 6
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkEmpRosterFieldVo.cs
  19. 11 7
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkFieldValueVo.cs
  20. 31 40
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkSendInteractiveCardsInput.cs
  21. 4 4
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkSendInteractiveCardsOutput.cs
  22. 3 3
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkSendInteractiveCardsResult.cs
  23. 8 12
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkCurrentEmployeesListInput.cs
  24. 9 6
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkCurrentEmployeesListOutput.cs
  25. 10 6
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkCurrentEmployeesRosterListInput.cs
  26. 15 13
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkToken.cs
  27. 6 2
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/IDingTalkApi.cs
  28. 26 0
      Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Startup.cs
  29. 1 1
      Admin.NET/Plugins/Admin.NET.Plugin.GoView/Const/GoViewConst.cs

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

@@ -33,6 +33,8 @@
 
   <ItemGroup>
     <ProjectReference Include="..\Admin.NET.Core\Admin.NET.Core.csproj" />
+    <ProjectReference Include="..\Plugins\Admin.NET.Plugin.DingTalk\Admin.NET.Plugin.DingTalk.csproj" />
+    <ProjectReference Include="..\Plugins\Admin.NET.Plugin.GoView\Admin.NET.Plugin.GoView.csproj" />
   </ItemGroup>
 
   <ItemGroup>

+ 1 - 1
Admin.NET/Admin.NET.Core/Const/SqlSugarConst.cs

@@ -27,4 +27,4 @@ public class SqlSugarConst
     /// 默认表主键
     /// </summary>
     public const string PrimaryKey = "Id";
-}
+}

+ 0 - 269
Admin.NET/Admin.NET.Core/DingTalk/DingTalkService.cs

@@ -1,269 +0,0 @@
-// 大名科技(天津)有限公司 版权所有
-//
-// 此源代码遵循位于源代码树根目录中的 LICENSE 文件的许可证
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动
-//
-// 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
-
-using Admin.NET.Core.Integrations;
-
-namespace Admin.NET.Core.DingTalk;
-/// <summary>
-/// 钉钉服务
-/// </summary>
-[ApiDescriptionSettings(Order = 999)]
-public class DingTalkService : IDynamicApiController, IScoped
-{
-    private readonly UserManager _userManager;
-    private readonly IDingTalkApi _dingTalkApi;
-    private readonly DingTalkOptions _dingTalkOptions;
-    private readonly SqlSugarRepository<SysDingTalkUser> _sysDingTalkUserRepo;
-    private readonly SqlSugarRepository<SysUser> _sysUserRep;
-    private readonly IJsonSerializerProvider _jsonSerializer;
-    public DingTalkService(
-        UserManager userManager,
-        IDingTalkApi dingTalkApi,
-        IOptions<DingTalkOptions> dingTalkOptions,
-        SqlSugarRepository<SysDingTalkUser> sysDingTalkUserRepo,
-        SqlSugarRepository<SysUser> sysUserRep,
-        IJsonSerializerProvider jsonSerializer
-        )
-    {
-        _userManager = userManager;
-        _dingTalkApi = dingTalkApi;
-        _dingTalkOptions = dingTalkOptions.Value;
-        _sysDingTalkUserRepo = sysDingTalkUserRepo;
-        _sysUserRep = sysUserRep;
-        _jsonSerializer = jsonSerializer;
-
-    }
-
-    /// <summary>
-    /// 同步钉钉用户(后面可以做到job中)
-    /// </summary>
-    /// <returns></returns>
-    [HttpGet]
-    [DisplayName("同步钉钉用户")]
-    public async Task SyncDingTalkUser()
-    {
-        // 获取token
-        var param = new GetDingTalkTokenInput()
-        {
-            AppKey = _dingTalkOptions.ClientId,
-            AppSecret = _dingTalkOptions.ClientSecret
-        };
-        var tokenRes = await _dingTalkApi.GetDingTalkToken(param);
-        if (tokenRes.ErrCode != 0)
-        {
-            throw Oops.Oh(tokenRes.ErrMsg);
-        }
-        var offset = 0;
-        while (offset >= 0)
-        {
-            // 获取用户id列表
-            var userIdsRes = await _dingTalkApi.GetDingTalkCurrentEmployeesList(tokenRes.AccessToken, new GetDingTalkCurrentEmployeesListInput
-            {
-                StatusList = "2,3,5,-1",
-                Size = 50,
-                Offset = offset
-            });
-            if (!userIdsRes.Success)
-            {
-                throw Oops.Oh(userIdsRes.ErrMsg);
-            }
-            // 根据用户id获取花名册
-            var rosterRes = await _dingTalkApi.GetDingTalkCurrentEmployeesRosterList(tokenRes.AccessToken, new GetDingTalkCurrentEmployeesRosterListInput()
-            {
-                UserIdList = string.Join(",", userIdsRes.Result.DataList),
-                FieldFilterList = $"{DingTalkFieldConst.NameField},{DingTalkFieldConst.JobNumberField},{DingTalkFieldConst.MobileField}",
-                AgentId = _dingTalkOptions.AgentId
-            });
-            if (!rosterRes.Success)
-            {
-                throw Oops.Oh(rosterRes.ErrMsg);
-            }
-            // 判断新增还是更新
-            var userIds = rosterRes.Result.Select(res => res.UserId).ToList();
-            var uDingTalkUser = await _sysDingTalkUserRepo.AsQueryable()
-                .Where(u => userIds.Contains(u.DingTalkUserId))
-                .ToListAsync();
-            // 需要更新的用户id
-            var uUserIds = uDingTalkUser.Select(u => u.DingTalkUserId);
-            // 需要新增的用户id
-            var iUserIds = userIds.Where(u => !uUserIds.Contains(u));
-
-            #region 保存到钉钉用户表
-            var iUser = rosterRes.Result
-                .Where(res => iUserIds.Contains(res.UserId))
-                .Select(res => new SysDingTalkUser
-                {
-                    DingTalkUserId = res.UserId,
-                    Name = res.FieldDataList
-                    .Where(f => f.FieldCode == DingTalkFieldConst.NameField)
-                    .Select(f => f.FieldValueList.Select(v => v.Value).FirstOrDefault())
-                    .FirstOrDefault(),
-                    Mobile = res.FieldDataList
-                    .Where(f => f.FieldCode == DingTalkFieldConst.MobileField)
-                    .Select(f => f.FieldValueList.Select(v => v.Value).FirstOrDefault())
-                    .FirstOrDefault(),
-                    JobNumber = res.FieldDataList
-                    .Where(f => f.FieldCode == DingTalkFieldConst.JobNumberField)
-                    .Select(f => f.FieldValueList.Select(v => v.Value).FirstOrDefault())
-                    .FirstOrDefault(),
-                }).ToList();
-            if (iUser.Count > 0)
-            {
-                var iUserRes = await _sysDingTalkUserRepo.AsInsertable(iUser).ExecuteCommandAsync();
-                if (iUserRes <= 0)
-                {
-                    throw Oops.Oh("保存钉钉用户错误");
-                }
-            }
-            #endregion
-
-            #region 更新钉钉用户
-            var uUser = rosterRes.Result
-            .Where(res => uUserIds.Contains(res.UserId))
-            .Select(res => new SysDingTalkUser
-            {
-                Id = uDingTalkUser.Where(d => d.DingTalkUserId == res.UserId).Select(d => d.Id).FirstOrDefault(),
-                DingTalkUserId = res.UserId,
-                Name = res.FieldDataList
-                    .Where(f => f.FieldCode == DingTalkFieldConst.NameField)
-                    .Select(f => f.FieldValueList.Select(v => v.Value).FirstOrDefault())
-                    .FirstOrDefault(),
-                Mobile = res.FieldDataList
-                    .Where(f => f.FieldCode == DingTalkFieldConst.MobileField)
-                    .Select(f => f.FieldValueList.Select(v => v.Value).FirstOrDefault())
-                    .FirstOrDefault(),
-                JobNumber = res.FieldDataList
-                    .Where(f => f.FieldCode == DingTalkFieldConst.JobNumberField)
-                    .Select(f => f.FieldValueList.Select(v => v.Value).FirstOrDefault())
-                    .FirstOrDefault(),
-            }).ToList();
-            if (uUser.Count > 0)
-            {
-                var uUserRes = await _sysDingTalkUserRepo.AsUpdateable(uUser)
-                .UpdateColumns(d => new
-                {
-                    d.DingTalkUserId,
-                    d.Name,
-                    d.Mobile,
-                    d.JobNumber,
-                    d.UpdateTime,
-                    d.UpdateUserName,
-                    d.UpdateUserId,
-                }).ExecuteCommandAsync();
-                if (uUserRes <= 0)
-                {
-                    throw Oops.Oh("更新钉钉用户错误");
-                }
-            }
-            #endregion
-
-            // 保存分页游标
-            if (userIdsRes.Result.NextCursor == null)
-            {
-                break;
-            }
-            offset = (int)userIdsRes.Result.NextCursor;
-        }
-
-
-        var sysUser = await _sysUserRep.AsQueryable().Select(x => new
-        {
-            x.Id,
-            x.Account,
-            x.Phone
-        }).ToListAsync();
-
-        var sysDingTalkUser = await _sysDingTalkUserRepo.AsQueryable()
-            .Where(d => sysUser.Any(u => u.Account == d.JobNumber))
-            .Select(x => new
-            {
-                x.Id,
-                x.JobNumber,
-                x.Mobile
-            }).ToListAsync();
-
-        // 更新钉钉用户中系统用户id
-        var uSysDingTalkUser = sysDingTalkUser.Select(d => new SysDingTalkUser
-        {
-            Id = d.Id,
-            SysUserId = sysUser.Where(u => u.Account == d.JobNumber).Select(u => u.Id).FirstOrDefault(),
-        }).ToList();
-        var uSysDingTalkUserRes = await _sysDingTalkUserRepo.AsUpdateable(uSysDingTalkUser)
-        .UpdateColumns(d => new
-        {
-            d.SysUserId,
-            d.UpdateTime,
-            d.UpdateUserName,
-            d.UpdateUserId,
-        }).ExecuteCommandAsync();
-        if (uSysDingTalkUserRes <= 0)
-        {
-            throw Oops.Oh("更新钉钉用户错误");
-        }
-        return;
-
-
-
-
-    }
-
-
-    /// <summary>
-    /// 获取企业内部应用的access_token
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [HttpGet]
-    [DisplayName("获取企业内部应用的access_token")]
-    public async Task<GetDingTalkTokenOutput> GetDingTalkToken([FromQuery] GetDingTalkTokenInput input)
-    {
-        return await _dingTalkApi.GetDingTalkToken(input);
-    }
-
-    /// <summary>
-    /// 获取在职员工列表
-    /// </summary>
-    /// <param name="access_token"></param>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [HttpPost]
-    [DisplayName("获取在职员工列表")]
-    public async Task<DingTalkBaseResponse<GetDingTalkCurrentEmployeesListOutput>> GetDingTalkCurrentEmployeesList(string access_token, [Required] GetDingTalkCurrentEmployeesListInput input)
-    {
-        return await _dingTalkApi.GetDingTalkCurrentEmployeesList(access_token, input);
-    }
-    /// <summary>
-    /// 获取员工花名册字段信息
-    /// </summary>
-    /// <param name="access_token"></param>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [HttpPost]
-    [DisplayName("获取员工花名册字段信息")]
-    public async Task<DingTalkBaseResponse<List<DingTalkEmpRosterFieldVo>>> GetDingTalkCurrentEmployeesRosterList(string access_token, [Required] GetDingTalkCurrentEmployeesRosterListInput input)
-    {
-        return await _dingTalkApi.GetDingTalkCurrentEmployeesRosterList(access_token, input);
-    }
-    /// <summary>
-    /// 发送钉钉互动卡片
-    /// </summary>
-    /// <param name="token"></param>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [HttpPost]
-    [DisplayName("给指定用户发送钉钉互动卡片")]
-    public async Task<DingTalkSendInteractiveCardsOutput> DingTalkSendInteractiveCards(string token, DingTalkSendInteractiveCardsInput input)
-    {
-        return await _dingTalkApi.DingTalkSendInteractiveCards(token, input);
-    }
-
-
-
-}
-
-

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

@@ -58,8 +58,7 @@ global using System.Linq.Expressions;
 global using System.Reflection;
 global using System.Runtime.InteropServices;
 global using System.Text;
-global using System.Text.Json.Serialization;
 global using System.Text.RegularExpressions;
 global using System.Web;
 global using UAParser;
-global using Yitter.IdGenerator;
+global using Yitter.IdGenerator;

+ 0 - 1
Admin.NET/Admin.NET.Web.Core/ProjectOptions.cs

@@ -39,7 +39,6 @@ public static class ProjectOptions
         services.AddConfigurableOptions<CryptogramOptions>();
         services.AddConfigurableOptions<SMSOptions>();
         services.AddConfigurableOptions<EventBusOptions>();
-        services.AddConfigurableOptions<DingTalkOptions>();
         services.Configure<IpRateLimitOptions>(App.Configuration.GetSection("IpRateLimiting"));
         services.Configure<IpRateLimitPolicies>(App.Configuration.GetSection("IpRateLimitPolicies"));
         services.Configure<ClientRateLimitOptions>(App.Configuration.GetSection("ClientRateLimiting"));

+ 7 - 0
Admin.NET/Admin.NET.sln

@@ -22,6 +22,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin.NET.Plugin.GoView", "
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin.NET.Plugin.Elsa", "Plugins\Admin.NET.Plugin.Elsa\Admin.NET.Plugin.Elsa.csproj", "{48EFC3A6-BDC0-4D05-819A-B1FB927FA4C8}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Admin.NET.Plugin.DingTalk", "Plugins\Admin.NET.Plugin.DingTalk\Admin.NET.Plugin.DingTalk.csproj", "{F6A002AD-CF7F-4771-8597-F12A50A93DAA}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -52,6 +54,10 @@ Global
 		{48EFC3A6-BDC0-4D05-819A-B1FB927FA4C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{48EFC3A6-BDC0-4D05-819A-B1FB927FA4C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{48EFC3A6-BDC0-4D05-819A-B1FB927FA4C8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F6A002AD-CF7F-4771-8597-F12A50A93DAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F6A002AD-CF7F-4771-8597-F12A50A93DAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F6A002AD-CF7F-4771-8597-F12A50A93DAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F6A002AD-CF7F-4771-8597-F12A50A93DAA}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -59,6 +65,7 @@ Global
 	GlobalSection(NestedProjects) = preSolution
 		{C4A288D5-0FAA-4F43-9072-B97635D7871D} = {76F70D22-8D53-468E-A3B6-1704666A1D71}
 		{48EFC3A6-BDC0-4D05-819A-B1FB927FA4C8} = {76F70D22-8D53-468E-A3B6-1704666A1D71}
+		{F6A002AD-CF7F-4771-8597-F12A50A93DAA} = {76F70D22-8D53-468E-A3B6-1704666A1D71}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {5CD801D7-984A-4F5C-8FA2-211B7A5EA9F3}

+ 28 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Admin.NET.Plugin.DingTalk.csproj

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>disable</Nullable>
+    <GenerateDocumentationFile>True</GenerateDocumentationFile>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+    <NoWarn>1701;1702;1591;8632</NoWarn>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+    <NoWarn>1701;1702;1591;8632</NoWarn>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Admin.NET.Core\Admin.NET.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Update="DingTalk.json">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+
+</Project>

+ 11 - 3
Admin.NET/Admin.NET.Core/Const/DingTalkFieldConst.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Const/DingTalkConst.cs

@@ -6,21 +6,29 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core;
+namespace Admin.NET.Plugin.DingTalk;
 
 /// <summary>
-/// 钉钉字段常量
+/// 钉钉相关常量
 /// </summary>
-public class DingTalkFieldConst
+[Const("钉钉相关常量")]
+public class DingTalkConst
 {
+    /// <summary>
+    /// API分组名称
+    /// </summary>
+    public const string GroupName = "钉钉【DingTalk】";
+
     /// <summary>
     /// 姓名
     /// </summary>
     public const string NameField = "sys00-name";
+
     /// <summary>
     /// 手机号
     /// </summary>
     public const string MobileField = "sys00-mobile";
+
     /// <summary>
     /// 工号
     /// </summary>

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

@@ -1,12 +1,10 @@
 {
   "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
+
   "DingTalk": {
     "AppId": "",
     "AgentId": "",
-    // 原 AppKey 和 SuiteKey
-    "ClientId": "xxxx",
-    // 原 AppSecret 和 SuiteSecret
-    "ClientSecret": "xxxx"
+    "ClientId": "xxxx", // 原 AppKey 和 SuiteKey
+    "ClientSecret": "xxxx" // 原 AppSecret 和 SuiteSecret
   }
-
 }

+ 15 - 17
Admin.NET/Admin.NET.Core/Entity/SysDingTalkUser.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Entity/DingTalkUser.cs

@@ -6,14 +6,13 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core;
+namespace Admin.NET.Plugin.DingTalk;
 
 /// <summary>
 /// 钉钉用户表
 /// </summary>
 [SugarTable(null, "钉钉用户表")]
-[SysTable]
-public class SysDingTalkUser : EntityBase
+public class DingTalkUser : EntityBase
 {
     /// <summary>
     /// 系统用户Id
@@ -50,25 +49,12 @@ public class SysDingTalkUser : EntityBase
     [MaxLength(64)]
     public string? Name { get; set; }
 
-    /// <summary>
-    /// 头像
-    /// </summary>
-    [SugarColumn(ColumnDescription = "头像", Length = 256)]
-    [MaxLength(256)]
-    public string? Avatar { get; set; }
-
     /// <summary>
     /// 手机号码
     /// </summary>
     [SugarColumn(ColumnDescription = "手机号码", Length = 16)]
     [MaxLength(16)]
     public string? Mobile { get; set; }
-    /// <summary>
-    /// 工号
-    /// </summary>
-    [SugarColumn(ColumnDescription = "工号", Length = 16)]
-    [MaxLength(16)]
-    public string? JobNumber { get; set; }
 
     /// <summary>
     /// 性别
@@ -76,5 +62,17 @@ public class SysDingTalkUser : EntityBase
     [SugarColumn(ColumnDescription = "性别")]
     public int? Sex { get; set; }
 
+    /// <summary>
+    /// 头像
+    /// </summary>
+    [SugarColumn(ColumnDescription = "头像", Length = 256)]
+    [MaxLength(256)]
+    public string? Avatar { get; set; }
 
-}
+    /// <summary>
+    /// 工号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "工号", Length = 16)]
+    [MaxLength(16)]
+    public string? JobNumber { get; set; }
+}

+ 5 - 3
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/DingTalkConversationTypeEnum.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Enum/DingTalkConversationTypeEnum.cs

@@ -6,11 +6,12 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
+
 /// <summary>
-/// 钉钉发送的会话类型
+/// 钉钉发送的会话类型枚举
 /// </summary>
-[Description("钉钉发送的会话类型")]
+[Description("钉钉发送的会话类型枚举")]
 public enum DingTalkConversationTypeEnum
 {
     /// <summary>
@@ -18,6 +19,7 @@ public enum DingTalkConversationTypeEnum
     /// </summary>
     [Description("单聊")]
     SingleChat = 0,
+
     /// <summary>
     /// 群聊
     /// </summary>

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

@@ -0,0 +1,23 @@
+// 大名科技(天津)有限公司 版权所有
+//
+// 此源代码遵循位于源代码树根目录中的 LICENSE 文件的许可证
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动
+//
+// 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
+
+global using Admin.NET.Core;
+global using Furion;
+global using Furion.ConfigurableOptions;
+global using Furion.DependencyInjection;
+global using Furion.DynamicApiController;
+global using Furion.FriendlyException;
+global using Microsoft.AspNetCore.Http;
+global using Microsoft.AspNetCore.Mvc;
+global using Microsoft.Extensions.Options;
+global using Newtonsoft.Json;
+global using SqlSugar;
+global using System.ComponentModel;
+global using System.ComponentModel.DataAnnotations;
+global using System.Data;
+global using System.Linq.Dynamic.Core;

+ 4 - 1
Admin.NET/Admin.NET.Core/Option/DingTalkOptions.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Option/DingTalkOptions.cs

@@ -6,7 +6,7 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core;
+namespace Admin.NET.Plugin.DingTalk;
 
 public sealed class DingTalkOptions : IConfigurableOptions
 {
@@ -14,14 +14,17 @@ public sealed class DingTalkOptions : IConfigurableOptions
     /// AppId
     /// </summary>
     public string AppId { get; set; }
+
     /// <summary>
     /// AgentId
     /// </summary>
     public string AgentId { get; set; }
+
     /// <summary>
     /// 原 AppKey 和 SuiteKey
     /// </summary>
     public string ClientId { get; set; }
+
     /// <summary>
     /// 原 AppSecret 和 SuiteSecret
     /// </summary>

+ 209 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/DingTalkService.cs

@@ -0,0 +1,209 @@
+// 大名科技(天津)有限公司 版权所有
+//
+// 此源代码遵循位于源代码树根目录中的 LICENSE 文件的许可证
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动
+//
+// 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
+
+namespace Admin.NET.Plugin.DingTalk;
+
+/// <summary>
+/// 钉钉服务
+/// </summary>
+[ApiDescriptionSettings(DingTalkConst.GroupName, Module = "DingTalk", Order = 100)]
+public class DingTalkService : IDynamicApiController, IScoped
+{
+    private readonly IDingTalkApi _dingTalkApi;
+    private readonly DingTalkOptions _dingTalkOptions;
+    private readonly SqlSugarRepository<DingTalkUser> _dingTalkUserRepo;
+    private readonly SqlSugarRepository<SysUser> _sysUserRep;
+
+    public DingTalkService(IDingTalkApi dingTalkApi,
+        IOptions<DingTalkOptions> dingTalkOptions,
+        SqlSugarRepository<DingTalkUser> dingTalkUserRepo,
+        SqlSugarRepository<SysUser> sysUserRep)
+    {
+        _dingTalkApi = dingTalkApi;
+        _dingTalkOptions = dingTalkOptions.Value;
+        _dingTalkUserRepo = dingTalkUserRepo;
+        _sysUserRep = sysUserRep;
+    }
+
+    /// <summary>
+    /// 同步钉钉用户
+    /// </summary>
+    /// <returns></returns>
+    [DisplayName("同步钉钉用户")]
+    public async Task SyncDingTalkUser()
+    {
+        var param = new GetDingTalkTokenInput()
+        {
+            AppKey = _dingTalkOptions.ClientId,
+            AppSecret = _dingTalkOptions.ClientSecret
+        };
+        var tokenRes = await _dingTalkApi.GetDingTalkToken(param);
+        if (tokenRes.ErrCode != 0)
+            throw Oops.Oh(tokenRes.ErrMsg);
+
+        var offset = 0;
+        while (offset >= 0)
+        {
+            // 获取用户Id列表
+            var userIdsRes = await _dingTalkApi.GetDingTalkCurrentEmployeesList(tokenRes.AccessToken, new GetDingTalkCurrentEmployeesListInput
+            {
+                StatusList = "2,3,5,-1",
+                Size = 50,
+                Offset = offset
+            });
+            if (!userIdsRes.Success)
+                throw Oops.Oh(userIdsRes.ErrMsg);
+
+            // 根据用户Id获取花名册
+            var rosterRes = await _dingTalkApi.GetDingTalkCurrentEmployeesRosterList(tokenRes.AccessToken, new GetDingTalkCurrentEmployeesRosterListInput()
+            {
+                UserIdList = string.Join(",", userIdsRes.Result.DataList),
+                FieldFilterList = $"{DingTalkConst.NameField},{DingTalkConst.JobNumberField},{DingTalkConst.MobileField}",
+                AgentId = _dingTalkOptions.AgentId
+            });
+            if (!rosterRes.Success)
+                throw Oops.Oh(rosterRes.ErrMsg);
+
+            // 判断新增还是更新
+            var userIds = rosterRes.Result.Select(u => u.UserId).ToList();
+            var uDingTalkUser = await _dingTalkUserRepo.AsQueryable()
+                .Where(u => userIds.Contains(u.DingTalkUserId))
+                .ToListAsync();
+
+            var uUserIds = uDingTalkUser.Select(u => u.DingTalkUserId); // 需要更新的用户Id
+            var iUserIds = userIds.Where(u => !uUserIds.Contains(u)); // 需要新增的用户Id
+
+            // 保存钉钉用户
+            var iUsers = rosterRes.Result
+                .Where(u => iUserIds.Contains(u.UserId))
+                .Select(u => new DingTalkUser
+                {
+                    DingTalkUserId = u.UserId,
+                    Name = u.FieldDataList.Where(m => m.FieldCode == DingTalkConst.NameField).Select(m => m.FieldValueList.Select(n => n.Value).FirstOrDefault()).FirstOrDefault(),
+                    Mobile = u.FieldDataList.Where(m => m.FieldCode == DingTalkConst.MobileField).Select(m => m.FieldValueList.Select(n => n.Value).FirstOrDefault()).FirstOrDefault(),
+                    JobNumber = u.FieldDataList.Where(m => m.FieldCode == DingTalkConst.JobNumberField).Select(m => m.FieldValueList.Select(n => n.Value).FirstOrDefault()).FirstOrDefault(),
+                }).ToList();
+            if (iUsers.Count > 0)
+            {
+                await _dingTalkUserRepo.AsInsertable(iUsers).ExecuteCommandAsync();
+            }
+
+            // 更新钉钉用户
+            var uUsers = rosterRes.Result
+                .Where(u => uUserIds.Contains(u.UserId))
+                .Select(u => new DingTalkUser
+                {
+                    Id = uDingTalkUser.Where(m => m.DingTalkUserId == u.UserId).Select(m => m.Id).FirstOrDefault(),
+                    DingTalkUserId = u.UserId,
+                    Name = u.FieldDataList.Where(m => m.FieldCode == DingTalkConst.NameField).Select(m => m.FieldValueList.Select(n => n.Value).FirstOrDefault()).FirstOrDefault(),
+                    Mobile = u.FieldDataList.Where(m => m.FieldCode == DingTalkConst.MobileField).Select(m => m.FieldValueList.Select(n => n.Value).FirstOrDefault()).FirstOrDefault(),
+                    JobNumber = u.FieldDataList.Where(m => m.FieldCode == DingTalkConst.JobNumberField).Select(m => m.FieldValueList.Select(n => n.Value).FirstOrDefault()).FirstOrDefault(),
+                }).ToList();
+            if (uUsers.Count > 0)
+            {
+                await _dingTalkUserRepo.AsUpdateable(uUsers).UpdateColumns(u => new
+                {
+                    u.DingTalkUserId,
+                    u.Name,
+                    u.Mobile,
+                    u.JobNumber,
+                    u.UpdateTime,
+                    u.UpdateUserName,
+                    u.UpdateUserId,
+                }).ExecuteCommandAsync();
+            }
+
+            // 保存分页游标
+            if (userIdsRes.Result.NextCursor == null)
+                break;
+            offset = (int)userIdsRes.Result.NextCursor;
+        }
+
+        var sysUser = await _sysUserRep.AsQueryable()
+            .Select(u => new
+            {
+                u.Id,
+                u.Account,
+                u.Phone
+            }).ToListAsync();
+        var dingTalkUser = await _dingTalkUserRepo.AsQueryable()
+            .Where(u => sysUser.Any(m => m.Account == u.JobNumber))
+            .Select(u => new
+            {
+                u.Id,
+                u.JobNumber,
+                u.Mobile
+            }).ToListAsync();
+
+        // 更新钉钉用户中系统用户Id
+        var uDingTalkUsers = dingTalkUser.Select(u => new DingTalkUser
+        {
+            Id = u.Id,
+            SysUserId = sysUser.Where(m => m.Account == u.JobNumber).Select(m => m.Id).FirstOrDefault(),
+        }).ToList();
+        if (uDingTalkUsers.Count > 0)
+        {
+            await _dingTalkUserRepo.AsUpdateable(uDingTalkUsers).UpdateColumns(u => new
+            {
+                u.SysUserId,
+                u.UpdateTime,
+                u.UpdateUserName,
+                u.UpdateUserId,
+            }).ExecuteCommandAsync();
+        }
+
+        return;
+    }
+
+    /// <summary>
+    /// 获取企业内部应用的access_token
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [DisplayName("获取企业内部应用的access_token")]
+    public async Task<GetDingTalkTokenOutput> GetDingTalkToken([FromQuery] GetDingTalkTokenInput input)
+    {
+        return await _dingTalkApi.GetDingTalkToken(input);
+    }
+
+    /// <summary>
+    /// 获取在职员工列表
+    /// </summary>
+    /// <param name="access_token"></param>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [DisplayName("获取在职员工列表")]
+    public async Task<DingTalkBaseResponse<GetDingTalkCurrentEmployeesListOutput>> GetDingTalkCurrentEmployeesList(string access_token, [Required] GetDingTalkCurrentEmployeesListInput input)
+    {
+        return await _dingTalkApi.GetDingTalkCurrentEmployeesList(access_token, input);
+    }
+
+    /// <summary>
+    /// 获取员工花名册字段信息
+    /// </summary>
+    /// <param name="access_token"></param>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [DisplayName("获取员工花名册字段信息")]
+    public async Task<DingTalkBaseResponse<List<DingTalkEmpRosterFieldVo>>> GetDingTalkCurrentEmployeesRosterList(string access_token, [Required] GetDingTalkCurrentEmployeesRosterListInput input)
+    {
+        return await _dingTalkApi.GetDingTalkCurrentEmployeesRosterList(access_token, input);
+    }
+
+    /// <summary>
+    /// 发送钉钉互动卡片
+    /// </summary>
+    /// <param name="token"></param>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [DisplayName("给指定用户发送钉钉互动卡片")]
+    public async Task<DingTalkSendInteractiveCardsOutput> DingTalkSendInteractiveCards(string token, DingTalkSendInteractiveCardsInput input)
+    {
+        return await _dingTalkApi.DingTalkSendInteractiveCards(token, input);
+    }
+}

+ 9 - 13
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/DingTalkBaseResponse.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkBaseResponse.cs

@@ -6,7 +6,7 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
 
 /// <summary>
 /// 钉钉基础响应结果
@@ -17,31 +17,27 @@ public class DingTalkBaseResponse<T>
     /// <summary>
     /// 返回结果
     /// </summary>
-    [JsonPropertyName("result")]
     public T Result { get; set; }
+
     /// <summary>
     /// 返回码
     /// </summary>
-    [JsonPropertyName("errcode")]
     public int ErrCode { get; set; }
+
     /// <summary>
     /// 返回码描述。
     /// </summary>
-    [JsonPropertyName("errmsg")]
     public string ErrMsg { get; set; }
+
     /// <summary>
     /// 是否调用成功
     /// </summary>
-    /// <remarks>
-    /// true:成功;
-    /// false:失败;
-    /// </remarks>
-    [JsonPropertyName("success")]
     public bool Success { get; set; }
+
     /// <summary>
-    /// 请求ID。
+    /// 请求Id
     /// </summary>
-    [JsonPropertyName("request_id")]
+    [Newtonsoft.Json.JsonProperty("request_id")]
+    [System.Text.Json.Serialization.JsonPropertyName("request_id")]
     public string RequestId { get; set; }
-
-}
+}

+ 4 - 4
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/DingTalkCardData.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkCardData.cs

@@ -6,7 +6,7 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
 
 /// <summary>
 /// 卡片公有数据
@@ -16,12 +16,11 @@ public class DingTalkCardData
     /// <summary>
     /// 卡片模板内容替换参数,普通文本类型。
     /// </summary>
-    [JsonPropertyName("cardParamMap")]
     public DingTalkCardParamMap CardParamMap { get; set; }
+
     /// <summary>
     /// 卡片模板内容替换参数,多媒体类型。
     /// </summary>
-    [JsonPropertyName("cardMediaIdParamMap")]
     public string CardMediaIdParamMap { get; set; }
 }
 
@@ -33,6 +32,7 @@ public class DingTalkCardParamMap
     /// <summary>
     /// 片模板内容替换参数
     /// </summary>
-    [JsonPropertyName("sys_full_json_obj")]
+    [Newtonsoft.Json.JsonProperty("sys_full_json_obj")]
+    [System.Text.Json.Serialization.JsonPropertyName("sys_full_json_obj")]
     public string SysFullJsonObj { get; set; }
 }

+ 15 - 8
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/DingTalkEmpFieldDataVo.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkEmpFieldDataVo.cs

@@ -6,28 +6,35 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
+
 public class DingTalkEmpFieldDataVo
 {
     /// <summary>
     /// 字段名称
     /// </summary>
-    [JsonPropertyName("field_name")]
+    [Newtonsoft.Json.JsonProperty("field_name")]
+    [System.Text.Json.Serialization.JsonPropertyName("field_name")]
     public string FieldName { get; set; }
+
     /// <summary>
     /// 字段标识
     /// </summary>
-    [JsonPropertyName("field_code")]
+    [Newtonsoft.Json.JsonProperty("field_code")]
+    [System.Text.Json.Serialization.JsonPropertyName("field_code")]
     public string FieldCode { get; set; }
+
     /// <summary>
     /// 分组标识
     /// </summary>
-    [JsonPropertyName("group_id")]
+    [Newtonsoft.Json.JsonProperty("group_id")]
+    [System.Text.Json.Serialization.JsonPropertyName("group_id")]
     public string GroupId { get; set; }
+
     /// <summary>
-    /// 
+    ///
     /// </summary>
-    [JsonPropertyName("field_value_list")]
+    [Newtonsoft.Json.JsonProperty("field_value_list")]
+    [System.Text.Json.Serialization.JsonPropertyName("field_value_list")]
     public List<DingTalkFieldValueVo> FieldValueList { get; set; }
-
-}
+}

+ 11 - 6
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/DingTalkEmpRosterFieldVo.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkEmpRosterFieldVo.cs

@@ -6,23 +6,28 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
 
 public class DingTalkEmpRosterFieldVo
 {
     /// <summary>
-    /// 企业的corpid
+    /// 企业的corpid
     /// </summary>
-    [JsonPropertyName("corp_id")]
+    [Newtonsoft.Json.JsonProperty("corp_id")]
+    [System.Text.Json.Serialization.JsonPropertyName("corp_id")]
     public string CorpId { get; set; }
+
     /// <summary>
     /// 返回的字段信息列表
     /// </summary>
-    [JsonPropertyName("field_data_list")]
+    [Newtonsoft.Json.JsonProperty("field_data_list")]
+    [System.Text.Json.Serialization.JsonPropertyName("field_data_list")]
     public List<DingTalkEmpFieldDataVo> FieldDataList { get; set; }
+
     /// <summary>
     /// 员工的userid
     /// </summary>
-    [JsonPropertyName("userid")]
+    [Newtonsoft.Json.JsonProperty("userid")]
+    [System.Text.Json.Serialization.JsonPropertyName("userid")]
     public string UserId { get; set; }
-}
+}

+ 11 - 7
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/DingTalkFieldValueVo.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkFieldValueVo.cs

@@ -6,24 +6,28 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
 
 public class DingTalkFieldValueVo
 {
-
     /// <summary>
     /// 第几条的明细标识,下标从0开始
     /// </summary>
-    [JsonPropertyName("item_index")]
+    [Newtonsoft.Json.JsonProperty("item_index")]
+    [System.Text.Json.Serialization.JsonPropertyName("item_index")]
     public int ItemIndex { get; set; }
+
     /// <summary>
-    /// 字段展示值,选项类型字段对应选项的value
+    /// 字段展示值,选项类型字段对应选项的value
     /// </summary>
-    [JsonPropertyName("label")]
+    [Newtonsoft.Json.JsonProperty("label")]
+    [System.Text.Json.Serialization.JsonPropertyName("label")]
     public string Label { get; set; }
+
     /// <summary>
-    /// 字段取值,选项类型字段对应选项的key
+    /// 字段取值,选项类型字段对应选项的key
     /// </summary>
-    [JsonPropertyName("value")]
+    [Newtonsoft.Json.JsonProperty("value")]
+    [System.Text.Json.Serialization.JsonPropertyName("value")]
     public string Value { get; set; }
 }

+ 31 - 40
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/DingTalkSendInteractiveCardsInput.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkSendInteractiveCardsInput.cs

@@ -6,17 +6,18 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
+
 public class DingTalkSendInteractiveCardsInput
 {
     /// <summary>
-    /// 互动卡片的消息模板ID
+    /// 互动卡片的消息模板Id
     /// </summary>
     [Required(ErrorMessage = "互动卡片的消息模板Id必填!")]
-    [JsonPropertyName("cardTemplateId")]
     public string? CardTemplateId { get; set; }
+
     /// <summary>
-    /// 群ID
+    /// 群Id
     /// </summary>
     /// <remarks>
     /// 1、基于群模板创建的群。
@@ -24,10 +25,10 @@ public class DingTalkSendInteractiveCardsInput
     /// 2、安装群聊酷应用的群。
     /// 企业内部应用,通过群内安装酷应用事件获取回调参数OpenConversationId参数值。
     /// </remarks>
-    [JsonPropertyName("openConversationId")]
     public string OpenConversationId { get; set; }
+
     /// <summary>
-    /// 接收人userId列表
+    /// 接收人userId列表
     /// </summary>
     /// <remarks>
     /// 单聊:receiverUserIdList填写用户ID,最大值20。
@@ -35,68 +36,63 @@ public class DingTalkSendInteractiveCardsInput
     /// receiverUserIdList参数不填写,表示当前群内所有用户可见
     /// </remarks>
     [Required(ErrorMessage = "接收人userId列表必填!")]
-    [JsonPropertyName("receiverUserIdList")]
     public List<string>? ReceiverUserIdList { get; set; }
+
     /// <summary>
     /// 唯一标示卡片的外部编码
     /// </summary>
     [Required(ErrorMessage = "唯一标示卡片的外部编码必填!")]
-    [JsonPropertyName("outTrackId")]
     public string? OutTrackId { get; set; }
+
     /// <summary>
     /// 机器人的编码
     /// </summary>
-    [JsonPropertyName("robotCode")]
     public string RobotCode { get; set; }
+
     /// <summary>
-    /// 发送的会话类型
+    /// 发送的会话类型
     /// </summary>
     [Required(ErrorMessage = "会话类型必填!")]
-    [JsonPropertyName("conversationType")]
     public DingTalkConversationTypeEnum? ConversationType { get; set; }
+
     /// <summary>
     /// 卡片回调时的路由Key,用于查询注册的callbackUrl
     /// </summary>
-    [JsonPropertyName("callbackRouteKey")]
     public string CallbackRouteKey { get; set; }
+
     /// <summary>
-    /// 卡片公有数据
+    /// 卡片公有数据
     /// </summary>
     [Required(ErrorMessage = "卡片公有数据必填!")]
-    [JsonPropertyName("cardData")]
     public DingTalkCardData CardData { get; set; }
 }
 
-
 public class GetDingTalkCardMessageReadStatusInput
 {
     /// <summary>
     /// 机器人的编码
     /// </summary>
-    [JsonPropertyName("robotCode")]
-    public string robotCode { set; get; }
+    public string RobotCode { set; get; }
+
     /// <summary>
     /// 消息唯一标识,可通过批量发送人与机器人会话中机器人消息接口返回参数中processQueryKey字段获取。
     /// </summary>
-    [JsonPropertyName("processQueryKey")]
-    public string processQueryKey { set; get; }
+    public string ProcessQueryKey { set; get; }
 }
 
 public class GetDingTalkCardMessageReadStatusOutput
 {
     /// <summary>
-    /// 消息发送状态
+    /// 消息发送状态,SUCCESS:成功、RECALLED:已撤回、PROCESSING: 处理中
     /// </summary>
-    /// <remarks>
-    /// SUCCESS:成功
-    /// RECALLED:已撤回
-    /// PROCESSING: 处理中
-    /// </remarks>
-    [JsonPropertyName("sendStatus")]
-    public string sendStatus { get; set; }
-    [JsonPropertyName("messageReadInfoList")]
-    public DingTalkCardMessageReadInfoList messageReadInfoList { get; set; }
+    public string SendStatus { get; set; }
+
+    /// <summary>
+    ///
+    /// </summary>
+    public DingTalkCardMessageReadInfoList MessageReadInfoList { get; set; }
 }
+
 /// <summary>
 /// 钉钉卡片消息已读情况
 /// </summary>
@@ -105,20 +101,15 @@ public class DingTalkCardMessageReadInfoList
     /// <summary>
     /// 消息接收者名称
     /// </summary>
-    [JsonPropertyName("name")]
-    public string name { set; get; }
+    public string Name { set; get; }
+
     /// <summary>
     /// 消息接收者的userId
     /// </summary>
-    [JsonPropertyName("userId")]
-    public string userId { set; get; }
+    public string UserId { set; get; }
+
     /// <summary>
-    /// 已读状态:
+    /// 已读状态,READ已读、UNREAD:未读
     /// </summary>
-    /// <remarks>
-    /// READ:已读
-    /// UNREAD:未读
-    /// </remarks>
-    [JsonPropertyName("readStatus")]
-    public string readStatus { set; get; }
+    public string ReadStatus { set; get; }
 }

+ 4 - 4
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/DingTalkSendInteractiveCardsOutput.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkSendInteractiveCardsOutput.cs

@@ -6,7 +6,8 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
+
 /// <summary>
 /// 发送钉钉互动卡片返回
 /// </summary>
@@ -15,11 +16,10 @@ public class DingTalkSendInteractiveCardsOutput
     /// <summary>
     /// 返回结果
     /// </summary>
-    [JsonPropertyName("success")]
     public bool Success { get; set; }
+
     /// <summary>
     /// 创建卡片结果
     /// </summary>
-    [JsonPropertyName("result")]
     public DingTalkSendInteractiveCardsResult Result { get; set; }
-}
+}

+ 3 - 3
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/DingTalkSendInteractiveCardsResult.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/DingTalkSendInteractiveCardsResult.cs

@@ -6,12 +6,12 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
+
 public class DingTalkSendInteractiveCardsResult
 {
     /// <summary>
-    /// 用于业务方后续查看已读列表的查询key
+    /// 用于业务方后续查看已读列表的查询key
     /// </summary>
-    [JsonPropertyName("processQueryKey")]
     public string ProcessQueryKey { get; set; }
 }

+ 8 - 12
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/GetDingTalkCurrentEmployeesListInput.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkCurrentEmployeesListInput.cs

@@ -6,31 +6,27 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
+
 /// <summary>
 /// 获取在职员工列表参数
 /// </summary>
 public class GetDingTalkCurrentEmployeesListInput
 {
     /// <summary>
-    /// 在职员工状态筛选,可以查询多个状态。不同状态之间使用英文逗号分隔。
+    /// 在职员工状态筛选,可以查询多个状态。不同状态之间使用英文逗号分隔。2:试用期、3:正式、5:待离职、-1:无状态
     /// </summary>
-    /// <remarks>
-    /// 2:试用期
-    /// 3:正式
-    /// 5:待离职
-    /// -1:无状态
-    /// </remarks>
-    [JsonPropertyName("status_list")]
+    [Newtonsoft.Json.JsonProperty("status_list")]
+    [System.Text.Json.Serialization.JsonPropertyName("status_list")]
     public string StatusList { get; set; }
+
     /// <summary>
     /// 分页游标,从0开始。根据返回结果里的next_cursor是否为空来判断是否还有下一页,且再次调用时offset设置成next_cursor的值。
     /// </summary>
-    [JsonPropertyName("offset")]
     public int Offset { get; set; }
+
     /// <summary>
     /// 分页大小,最大50。
     /// </summary>
-    [JsonPropertyName("size")]
     public int Size { get; set; }
-}
+}

+ 9 - 6
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/GetDingTalkCurrentEmployeesListOutput.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkCurrentEmployeesListOutput.cs

@@ -6,18 +6,21 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
+
 public class GetDingTalkCurrentEmployeesListOutput
 {
     /// <summary>
-    /// 查询到的员工userid列表
+    /// 查询到的员工userId列表
     /// </summary>
-    [JsonPropertyName("data_list")]
+    [Newtonsoft.Json.JsonProperty("data_list")]
+    [System.Text.Json.Serialization.JsonPropertyName("data_list")]
     public List<string> DataList { get; set; }
+
     /// <summary>
     /// 下一次分页调用的offset值,当返回结果里没有next_cursor时,表示分页结束。
     /// </summary>
-    [JsonPropertyName("next_cursor")]
+    [Newtonsoft.Json.JsonProperty("next_cursor")]
+    [System.Text.Json.Serialization.JsonPropertyName("next_cursor")]
     public int? NextCursor { get; set; }
-
-}
+}

+ 10 - 6
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/GetDingTalkCurrentEmployeesRosterListInput.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkCurrentEmployeesRosterListInput.cs

@@ -6,22 +6,26 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
+
 public class GetDingTalkCurrentEmployeesRosterListInput
 {
     /// <summary>
-    /// 员工的userid列表,多个userid之间使用逗号分隔,一次最多支持传100个值。
+    /// 员工的userId列表,多个userid之间使用逗号分隔,一次最多支持传100个值。
     /// </summary>
-    [JsonPropertyName("userid_list")]
+    [Newtonsoft.Json.JsonProperty("userid_list")]
+    [System.Text.Json.Serialization.JsonPropertyName("userid_list")]
     public string UserIdList { get; set; }
+
     /// <summary>
     /// 需要获取的花名册字段field_code值列表,多个字段之间使用逗号分隔,一次最多支持传100个值。
     /// </summary>
-    [JsonPropertyName("field_filter_list")]
+    [Newtonsoft.Json.JsonProperty("field_filter_list")]
+    [System.Text.Json.Serialization.JsonPropertyName("field_filter_list")]
     public string FieldFilterList { get; set; }
+
     /// <summary>
     /// 应用的AgentId
     /// </summary>
-    [JsonPropertyName("agentid")]
     public string AgentId { get; set; }
-}
+}

+ 15 - 13
Admin.NET/Admin.NET.Core/Integrations/DingTalk/Dto/GetDingTalkToken.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/Dto/GetDingTalkToken.cs

@@ -6,42 +6,44 @@
 //
 // 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
+
 public class GetDingTalkTokenInput
 {
     /// <summary>
-    /// 应用的唯一标识key 
+    /// 应用的唯一标识key
     /// </summary>
-    [JsonPropertyName("appkey")]
     public string AppKey { get; set; }
+
     /// <summary>
     /// 应用的密钥。AppKey和AppSecret可在钉钉开发者后台的应用详情页面获取。
     /// </summary>
-    [JsonPropertyName("appsecret")]
     public string AppSecret { get; set; }
 }
 
 public class GetDingTalkTokenOutput
 {
     /// <summary>
-    /// 生成的access_token 
+    /// 生成的access_token
     /// </summary>
-    [JsonPropertyName("access_token")]
+    [Newtonsoft.Json.JsonProperty("access_token")]
+    [System.Text.Json.Serialization.JsonPropertyName("access_token")]
     public string AccessToken { get; set; }
+
     /// <summary>
     /// access_token的过期时间,单位秒
     /// </summary>
-    [JsonPropertyName("expires_in")]
+    [Newtonsoft.Json.JsonProperty("expires_in")]
+    [System.Text.Json.Serialization.JsonPropertyName("expires_in")]
     public int ExpiresIn { get; set; }
+
     /// <summary>
-    /// 返回码描述
+    /// 返回码描述
     /// </summary>
-    [JsonPropertyName("errmsg")]
     public string ErrMsg { get; set; }
+
     /// <summary>
-    /// 返回码
+    /// 返回码
     /// </summary>
-    [JsonPropertyName("errcode")]
     public int ErrCode { get; set; }
-}
-
+}

+ 6 - 2
Admin.NET/Admin.NET.Core/Integrations/DingTalk/IDingTalkApi.cs → Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Service/IDingTalkApi.cs

@@ -8,7 +8,8 @@
 
 using Furion.RemoteRequest;
 
-namespace Admin.NET.Core.Integrations;
+namespace Admin.NET.Plugin.DingTalk;
+
 public interface IDingTalkApi : IHttpDispatchProxy
 {
     /// <summary>
@@ -17,6 +18,7 @@ public interface IDingTalkApi : IHttpDispatchProxy
     /// <returns></returns>
     [Get("https://oapi.dingtalk.com/gettoken")]
     Task<GetDingTalkTokenOutput> GetDingTalkToken([QueryString] GetDingTalkTokenInput input);
+
     /// <summary>
     /// 获取在职员工列表
     /// </summary>
@@ -26,6 +28,7 @@ public interface IDingTalkApi : IHttpDispatchProxy
     [Post("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/queryonjob")]
     Task<DingTalkBaseResponse<GetDingTalkCurrentEmployeesListOutput>> GetDingTalkCurrentEmployeesList([QueryString] string access_token,
         [Body, Required] GetDingTalkCurrentEmployeesListInput input);
+
     /// <summary>
     /// 获取员工花名册字段信息
     /// </summary>
@@ -35,6 +38,7 @@ public interface IDingTalkApi : IHttpDispatchProxy
     [Post("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/v2/list")]
     Task<DingTalkBaseResponse<List<DingTalkEmpRosterFieldVo>>> GetDingTalkCurrentEmployeesRosterList([QueryString] string access_token,
         [Body, Required] GetDingTalkCurrentEmployeesRosterListInput input);
+
     /// <summary>
     /// 发送钉钉互动卡片
     /// </summary>
@@ -45,6 +49,7 @@ public interface IDingTalkApi : IHttpDispatchProxy
     Task<DingTalkSendInteractiveCardsOutput> DingTalkSendInteractiveCards(
         [Headers("x-acs-dingtalk-access-token")] string token,
         [Body] DingTalkSendInteractiveCardsInput input);
+
     /// <summary>
     /// 获取钉钉卡片消息读取状态
     /// </summary>
@@ -55,5 +60,4 @@ public interface IDingTalkApi : IHttpDispatchProxy
     Task<GetDingTalkCardMessageReadStatusOutput> GetDingTalkCardMessageReadStatus(
     [Headers("x-acs-dingtalk-access-token")] string token,
     [QueryString] GetDingTalkCardMessageReadStatusInput input);
-
 }

+ 26 - 0
Admin.NET/Plugins/Admin.NET.Plugin.DingTalk/Startup.cs

@@ -0,0 +1,26 @@
+// 大名科技(天津)有限公司 版权所有
+//
+// 此源代码遵循位于源代码树根目录中的 LICENSE 文件的许可证
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动
+//
+// 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Admin.NET.Plugin.DingTalk;
+
+[AppStartup(100)]
+public class Startup : AppStartup
+{
+    public void ConfigureServices(IServiceCollection services)
+    {
+        services.AddConfigurableOptions<DingTalkOptions>();
+    }
+
+    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+    {
+    }
+}

+ 1 - 1
Admin.NET/Plugins/Admin.NET.Plugin.GoView/Const/GoViewConst.cs

@@ -17,5 +17,5 @@ public class GoViewConst
     /// <summary>
     /// API分组名称
     /// </summary>
-    public const string GroupName = "GoView";
+    public const string GroupName = "可视化大屏【GoView";
 }