526 lines
21 KiB
C#
526 lines
21 KiB
C#
|
|
using Microsoft.Extensions.Logging;
|
|||
|
|
using Newtonsoft.Json;
|
|||
|
|
using System.Globalization;
|
|||
|
|
using System.Net.Http.Headers;
|
|||
|
|
using System.Text;
|
|||
|
|
|
|||
|
|
namespace VOL.HR.Services.DingTalk
|
|||
|
|
{
|
|||
|
|
public class DingTalkService
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
private readonly string _corpId;
|
|||
|
|
private readonly string _appKey;
|
|||
|
|
private readonly string _appSecret;
|
|||
|
|
private readonly string _agentId;
|
|||
|
|
private string _accessToken;
|
|||
|
|
private DateTime _tokenExpiry;
|
|||
|
|
private readonly SemaphoreSlim _tokenLock = new SemaphoreSlim(1, 1);
|
|||
|
|
private readonly ILogger<DingTalkService> _logger;
|
|||
|
|
|
|||
|
|
// 钉钉API地址
|
|||
|
|
private readonly string _tokenUrl;
|
|||
|
|
private const string DepartmentSubUrl = "https://oapi.dingtalk.com/topapi/v2/department/listsub";
|
|||
|
|
private const string UserListIdUrl = "https://oapi.dingtalk.com/topapi/user/listid";
|
|||
|
|
private const string RosterUrl = "https://api.dingtalk.com/v1.0/hrm/rosters/lists/query";
|
|||
|
|
private const string UpdateEmployeeUrl = "https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/v2/update";
|
|||
|
|
|
|||
|
|
// 需要获取的花名册字段代码
|
|||
|
|
private readonly List<string> _fieldCodes = new List<string>
|
|||
|
|
{
|
|||
|
|
"sys00-name", // 姓名
|
|||
|
|
"sys00-email", // 邮箱
|
|||
|
|
"sys00-mobile", // 手机号
|
|||
|
|
"sys02-certNo", // 证件号码
|
|||
|
|
"sys02-birthTime", // 出生日期
|
|||
|
|
"sys02-sexType", // 性别
|
|||
|
|
"sys00-dept", // 部门
|
|||
|
|
"sys00-jobNumber" // 工号
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
public DingTalkService(string corpId, string appKey, string appSecret, string agentId, ILogger<DingTalkService> logger)
|
|||
|
|
{
|
|||
|
|
_corpId = corpId;
|
|||
|
|
_appKey = appKey;
|
|||
|
|
_appSecret = appSecret;
|
|||
|
|
_agentId = agentId;
|
|||
|
|
_tokenUrl = $"https://api.dingtalk.com/v1.0/oauth2/{_corpId}/token";
|
|||
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task<string> GetAccessTokenAsync()
|
|||
|
|
{
|
|||
|
|
var payload = new Dictionary<string, string>
|
|||
|
|
{
|
|||
|
|
{ "client_id", _appKey },
|
|||
|
|
{ "client_secret", _appSecret },
|
|||
|
|
{ "grant_type", "client_credentials" }
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
using (var client = new HttpClient())
|
|||
|
|
{
|
|||
|
|
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
|||
|
|
var jsonPayload = JsonConvert.SerializeObject(payload);
|
|||
|
|
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var response = await client.PostAsync(_tokenUrl, content);
|
|||
|
|
response.EnsureSuccessStatusCode();
|
|||
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|||
|
|
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseContent);
|
|||
|
|
return data["access_token"];
|
|||
|
|
}
|
|||
|
|
catch (HttpRequestException ex)
|
|||
|
|
{
|
|||
|
|
_logger.LogError($"获取access_token失败: {ex.Message}");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task<string> GetValidTokenAsync()
|
|||
|
|
{
|
|||
|
|
await _tokenLock.WaitAsync();
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 如果token不存在或已过期,重新获取
|
|||
|
|
if (string.IsNullOrEmpty(_accessToken) || DateTime.Now >= _tokenExpiry)
|
|||
|
|
{
|
|||
|
|
_logger.LogInformation("钉钉access_token已过期,重新获取...");
|
|||
|
|
var token = await GetAccessTokenAsync();
|
|||
|
|
if (token != null)
|
|||
|
|
{
|
|||
|
|
_accessToken = token;
|
|||
|
|
// 钉钉token有效期为7200秒,提前300秒刷新
|
|||
|
|
_tokenExpiry = DateTime.Now + TimeSpan.FromSeconds(6900);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
_logger.LogError("无法获取有效的钉钉access_token");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return _accessToken;
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
_tokenLock.Release();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task<List<Dictionary<string, object>>> GetSubDepartmentsAsync(long parentId = 1)
|
|||
|
|
{
|
|||
|
|
var accessToken = await GetValidTokenAsync();
|
|||
|
|
if (accessToken == null)
|
|||
|
|
{
|
|||
|
|
return new List<Dictionary<string, object>>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var departments = new List<Dictionary<string, object>>();
|
|||
|
|
var payload = new Dictionary<string, object> { { "dept_id", parentId } };
|
|||
|
|
var queryParams = new Dictionary<string, string> { { "access_token", accessToken } };
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
using (var client = new HttpClient())
|
|||
|
|
{
|
|||
|
|
var jsonPayload = JsonConvert.SerializeObject(payload);
|
|||
|
|
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
|
|||
|
|
var uriBuilder = new UriBuilder(DepartmentSubUrl);
|
|||
|
|
uriBuilder.Query = string.Join("&", queryParams.Select(kvp => $"{kvp.Key}={kvp.Value}"));
|
|||
|
|
|
|||
|
|
var response = await client.PostAsync(uriBuilder.Uri, content);
|
|||
|
|
response.EnsureSuccessStatusCode();
|
|||
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|||
|
|
var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseContent);
|
|||
|
|
|
|||
|
|
if (Convert.ToInt32(data["errcode"]) == 0)
|
|||
|
|
{
|
|||
|
|
var result = (Newtonsoft.Json.Linq.JArray)data["result"];
|
|||
|
|
foreach (Newtonsoft.Json.Linq.JObject dept in result)
|
|||
|
|
{
|
|||
|
|
long deptId = (long)dept["dept_id"];
|
|||
|
|
var department = new Dictionary<string, object>
|
|||
|
|
{
|
|||
|
|
{ "id", deptId },
|
|||
|
|
{ "name", dept["name"].ToString() },
|
|||
|
|
{ "parent_id", (long)dept["parent_id"] }
|
|||
|
|
};
|
|||
|
|
departments.Add(department);
|
|||
|
|
|
|||
|
|
// 递归获取子部门
|
|||
|
|
departments.AddRange(await GetSubDepartmentsAsync(deptId));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return departments;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (HttpRequestException ex)
|
|||
|
|
{
|
|||
|
|
_logger.LogError($"获取部门列表失败: {ex.Message}");
|
|||
|
|
return new List<Dictionary<string, object>>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task<List<string>> GetDeptUserIdsAsync(long deptId)
|
|||
|
|
{
|
|||
|
|
var accessToken = await GetValidTokenAsync();
|
|||
|
|
if (accessToken == null)
|
|||
|
|
{
|
|||
|
|
return new List<string>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var payload = new Dictionary<string, object> { { "dept_id", deptId } };
|
|||
|
|
var queryParams = new Dictionary<string, string> { { "access_token", accessToken } };
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
using (var client = new HttpClient())
|
|||
|
|
{
|
|||
|
|
var jsonPayload = JsonConvert.SerializeObject(payload);
|
|||
|
|
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
|
|||
|
|
var uriBuilder = new UriBuilder(UserListIdUrl);
|
|||
|
|
uriBuilder.Query = string.Join("&", queryParams.Select(kvp => $"{kvp.Key}={kvp.Value}"));
|
|||
|
|
|
|||
|
|
var response = await client.PostAsync(uriBuilder.Uri, content);
|
|||
|
|
response.EnsureSuccessStatusCode();
|
|||
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|||
|
|
var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseContent);
|
|||
|
|
|
|||
|
|
if (Convert.ToInt32(data["errcode"]) == 0)
|
|||
|
|
{
|
|||
|
|
var result = (Newtonsoft.Json.Linq.JObject)data["result"];
|
|||
|
|
return ((Newtonsoft.Json.Linq.JArray)result["userid_list"]).Select(x => x.ToString()).ToList();
|
|||
|
|
}
|
|||
|
|
return new List<string>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (HttpRequestException ex)
|
|||
|
|
{
|
|||
|
|
_logger.LogError($"获取部门用户失败: {ex.Message}");
|
|||
|
|
return new List<string>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task<List<Dictionary<string, object>>> GetRosterInfoAsync(List<string> userIds)
|
|||
|
|
{
|
|||
|
|
var accessToken = await GetValidTokenAsync();
|
|||
|
|
if (accessToken == null)
|
|||
|
|
{
|
|||
|
|
return new List<Dictionary<string, object>>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var headers = new Dictionary<string, string>
|
|||
|
|
{
|
|||
|
|
{ "x-acs-dingtalk-access-token", accessToken },
|
|||
|
|
{ "Content-Type", "application/json" }
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
var payload = new Dictionary<string, object>
|
|||
|
|
{
|
|||
|
|
{ "userIdList", userIds },
|
|||
|
|
{ "fieldFilterList", _fieldCodes },
|
|||
|
|
{ "appAgentId", _agentId },
|
|||
|
|
{ "text2SelectConvert", true }
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
using (var client = new HttpClient())
|
|||
|
|
{
|
|||
|
|
foreach (var header in headers)
|
|||
|
|
{
|
|||
|
|
client.DefaultRequestHeaders.Add(header.Key, header.Value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var jsonPayload = JsonConvert.SerializeObject(payload);
|
|||
|
|
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
|
|||
|
|
|
|||
|
|
var response = await client.PostAsync(RosterUrl, content);
|
|||
|
|
response.EnsureSuccessStatusCode();
|
|||
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|||
|
|
var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseContent);
|
|||
|
|
return ((Newtonsoft.Json.Linq.JArray)data["result"]).ToObject<List<Dictionary<string, object>>>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (HttpRequestException ex)
|
|||
|
|
{
|
|||
|
|
_logger.LogError($"获取花名册失败: {ex.Message}");
|
|||
|
|
return new List<Dictionary<string, object>>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public List<Dictionary<string, string>> ExtractEmployeeData(List<Dictionary<string, object>> rosterData)
|
|||
|
|
{
|
|||
|
|
var employees = new List<Dictionary<string, string>>();
|
|||
|
|
|
|||
|
|
foreach (var userData in rosterData)
|
|||
|
|
{
|
|||
|
|
var emp = new Dictionary<string, string> { { "userid", userData["userId"].ToString() } };
|
|||
|
|
|
|||
|
|
var fieldDataList = (Newtonsoft.Json.Linq.JArray)userData["fieldDataList"];
|
|||
|
|
|
|||
|
|
foreach (Newtonsoft.Json.Linq.JObject field in fieldDataList)
|
|||
|
|
{
|
|||
|
|
string fieldCode = field["fieldCode"].ToString();
|
|||
|
|
string fieldValue = "";
|
|||
|
|
|
|||
|
|
var fieldValueList = (Newtonsoft.Json.Linq.JArray)field["fieldValueList"];
|
|||
|
|
if (fieldValueList != null && fieldValueList.Count > 0)
|
|||
|
|
{
|
|||
|
|
// 处理部门信息
|
|||
|
|
if (fieldCode == "sys00-dept")
|
|||
|
|
{
|
|||
|
|
// 部门可能有多个,取第一个
|
|||
|
|
fieldValue = field["fieldValueList"][0]["label"].ToString();
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
fieldValue = field["fieldValueList"][0]["label"].ToString();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 映射字段到中文名
|
|||
|
|
switch (fieldCode)
|
|||
|
|
{
|
|||
|
|
case "sys00-name":
|
|||
|
|
emp["姓名"] = fieldValue;
|
|||
|
|
break;
|
|||
|
|
case "sys00-email":
|
|||
|
|
emp["邮箱"] = fieldValue;
|
|||
|
|
break;
|
|||
|
|
case "sys00-dept":
|
|||
|
|
emp["部门"] = fieldValue;
|
|||
|
|
break;
|
|||
|
|
case "sys00-mobile":
|
|||
|
|
emp["手机号"] = fieldValue;
|
|||
|
|
break;
|
|||
|
|
case "sys02-certNo":
|
|||
|
|
emp["证件号码"] = fieldValue;
|
|||
|
|
break;
|
|||
|
|
case "sys02-birthTime":
|
|||
|
|
emp["出生日期"] = fieldValue;
|
|||
|
|
break;
|
|||
|
|
case "sys02-sexType":
|
|||
|
|
emp["性别"] = fieldValue;
|
|||
|
|
break;
|
|||
|
|
case "sys00-jobNumber":
|
|||
|
|
emp["工号"] = fieldValue;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
employees.Add(emp);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return employees;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task<(List<Dictionary<string, string>>, List<Dictionary<string, object>>)> GetAllEmployeesAsync()
|
|||
|
|
{
|
|||
|
|
DateTime startTime = DateTime.Now;
|
|||
|
|
|
|||
|
|
_logger.LogInformation("正在获取钉钉访问凭证...");
|
|||
|
|
var accessToken = await GetValidTokenAsync();
|
|||
|
|
if (accessToken == null)
|
|||
|
|
{
|
|||
|
|
return (new List<Dictionary<string, string>>(), new List<Dictionary<string, object>>());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_logger.LogInformation("正在获取部门结构...");
|
|||
|
|
var departments = await GetSubDepartmentsAsync();
|
|||
|
|
_logger.LogInformation($"共获取到 {departments.Count} 个部门");
|
|||
|
|
|
|||
|
|
HashSet<string> allUserIds = new HashSet<string>();
|
|||
|
|
_logger.LogInformation("正在获取部门用户列表...");
|
|||
|
|
foreach (var dept in departments)
|
|||
|
|
{
|
|||
|
|
var userIds = await GetDeptUserIdsAsync(Convert.ToInt64(dept["id"]));
|
|||
|
|
allUserIds.UnionWith(userIds);
|
|||
|
|
_logger.LogInformation($"部门 {dept["name"]} 有 {userIds.Count} 个用户");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<string> userIdList = allUserIds.ToList();
|
|||
|
|
_logger.LogInformation($"共获取到 {userIdList.Count} 个用户");
|
|||
|
|
|
|||
|
|
// 分批处理用户(每批100人)
|
|||
|
|
List<Dictionary<string, string>> allEmployees = new List<Dictionary<string, string>>();
|
|||
|
|
int batchSize = 100;
|
|||
|
|
for (int i = 0; i < userIdList.Count; i += batchSize)
|
|||
|
|
{
|
|||
|
|
var batch = userIdList.GetRange(i, Math.Min(batchSize, userIdList.Count - i));
|
|||
|
|
_logger.LogInformation($"正在处理用户批次 {i / batchSize + 1}/{(userIdList.Count - 1) / batchSize + 1}");
|
|||
|
|
|
|||
|
|
var rosterData = await GetRosterInfoAsync(batch);
|
|||
|
|
var employees = ExtractEmployeeData(rosterData);
|
|||
|
|
allEmployees.AddRange(employees);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_logger.LogInformation($"成功获取 {allEmployees.Count} 名员工信息");
|
|||
|
|
_logger.LogInformation($"钉钉数据获取完成,耗时: {DateTime.Now.Subtract(startTime).TotalSeconds:.2f}秒");
|
|||
|
|
|
|||
|
|
return (allEmployees, departments);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task<bool> UpdateEmployeeAsync(string userId, Dictionary<string, Dictionary<string, string>> fieldUpdates)
|
|||
|
|
{
|
|||
|
|
var accessToken = await GetValidTokenAsync();
|
|||
|
|
if (accessToken == null)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 构建更新参数 - 根据API文档调整结构
|
|||
|
|
var groups = new List<Dictionary<string, object>>();
|
|||
|
|
foreach (var groupId in fieldUpdates.Keys)
|
|||
|
|
{
|
|||
|
|
var fields = fieldUpdates[groupId];
|
|||
|
|
var fieldList = new List<Dictionary<string, object>>();
|
|||
|
|
foreach (var fieldCode in fields.Keys)
|
|||
|
|
{
|
|||
|
|
string value = fields[fieldCode];
|
|||
|
|
// 处理特殊字段类型
|
|||
|
|
object processedValue = value;
|
|||
|
|
|
|||
|
|
// 日期字段需要特殊处理
|
|||
|
|
if (fieldCode == "sys02-birthTime" && !string.IsNullOrEmpty(value))
|
|||
|
|
{
|
|||
|
|
// 确保日期格式正确
|
|||
|
|
processedValue = FormatDate(value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 性别字段需要转换
|
|||
|
|
if (fieldCode == "sys02-sexType")
|
|||
|
|
{
|
|||
|
|
processedValue = ConvertGenderForDingTalk(value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fieldList.Add(new Dictionary<string, object>
|
|||
|
|
{
|
|||
|
|
{ "field_code", fieldCode },
|
|||
|
|
{ "value", processedValue }
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
groups.Add(new Dictionary<string, object>
|
|||
|
|
{
|
|||
|
|
{ "group_id", groupId },
|
|||
|
|
{
|
|||
|
|
"sections", new List<Dictionary<string, object>>
|
|||
|
|
{
|
|||
|
|
new Dictionary<string, object>
|
|||
|
|
{
|
|||
|
|
{ "section", fieldList }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var queryParams = new Dictionary<string, string> { { "access_token", accessToken } };
|
|||
|
|
var payload = new Dictionary<string, object>
|
|||
|
|
{
|
|||
|
|
{ "agentid", _agentId },
|
|||
|
|
{
|
|||
|
|
"param", new Dictionary<string, object>
|
|||
|
|
{
|
|||
|
|
{ "userid", userId },
|
|||
|
|
{ "groups", groups }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
_logger.LogInformation($"钉钉更新请求: {JsonConvert.SerializeObject(payload, Formatting.None)}");
|
|||
|
|
|
|||
|
|
using (var client = new HttpClient())
|
|||
|
|
{
|
|||
|
|
var uriBuilder = new UriBuilder(UpdateEmployeeUrl);
|
|||
|
|
uriBuilder.Query = string.Join("&", queryParams.Select(kvp => $"{kvp.Key}={kvp.Value}"));
|
|||
|
|
|
|||
|
|
var jsonPayload = JsonConvert.SerializeObject(payload);
|
|||
|
|
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
|
|||
|
|
|
|||
|
|
var response = await client.PostAsync(uriBuilder.Uri, content);
|
|||
|
|
_logger.LogInformation($"钉钉更新响应状态: {response.StatusCode}");
|
|||
|
|
_logger.LogInformation($"钉钉更新响应内容: {await response.Content.ReadAsStringAsync()}");
|
|||
|
|
|
|||
|
|
response.EnsureSuccessStatusCode();
|
|||
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|||
|
|
var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseContent);
|
|||
|
|
|
|||
|
|
// 根据钉钉API文档检查响应
|
|||
|
|
if (Convert.ToInt32(data.GetValueOrDefault("errcode", -1)) == 0 &&
|
|||
|
|
Convert.ToBoolean(data.GetValueOrDefault("result", false)) &&
|
|||
|
|
Convert.ToBoolean(data.GetValueOrDefault("success", false)))
|
|||
|
|
{
|
|||
|
|
_logger.LogInformation($"员工 {userId} 更新成功");
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
string errorMsg = data.GetValueOrDefault("errmsg", "未知错误")?.ToString();
|
|||
|
|
_logger.LogError($"员工更新失败: {errorMsg}");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (HttpRequestException ex)
|
|||
|
|
{
|
|||
|
|
_logger.LogError($"更新员工信息失败: {ex.Message}");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private string FormatDate(string dateStr)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 尝试解析各种日期格式
|
|||
|
|
if (!string.IsNullOrEmpty(dateStr))
|
|||
|
|
{
|
|||
|
|
// 移除时间部分(如果有)
|
|||
|
|
if (dateStr.Contains(" "))
|
|||
|
|
{
|
|||
|
|
dateStr = dateStr.Split(" ")[0];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 尝试解析常见日期格式
|
|||
|
|
foreach (string fmt in new string[] { "yyyy-MM-dd", "yyyy/MM/dd", "yyyy.MM.dd", "yyyy年MM月dd日" })
|
|||
|
|
{
|
|||
|
|
if (DateTime.TryParseExact(dateStr, fmt, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateObj))
|
|||
|
|
{
|
|||
|
|
return dateObj.ToString("yyyy-MM-dd");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return dateStr;
|
|||
|
|
}
|
|||
|
|
catch
|
|||
|
|
{
|
|||
|
|
return dateStr;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private string ConvertGenderForDingTalk(string gender)
|
|||
|
|
{
|
|||
|
|
if (gender == "男")
|
|||
|
|
{
|
|||
|
|
return "1";
|
|||
|
|
}
|
|||
|
|
else if (gender == "女")
|
|||
|
|
{
|
|||
|
|
return "2";
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
return gender;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|