2025-09-25 14:37:10 +08:00
|
|
|
|
/*
|
2025-09-26 13:35:45 +08:00
|
|
|
|
*所有关于HR_DeptSync类的业务代码应在此处编写
|
2025-09-25 14:37:10 +08:00
|
|
|
|
*可使用repository.调用常用方法,获取EF/Dapper等信息
|
|
|
|
|
|
*如果需要事务请使用repository.DbContextBeginTransaction
|
|
|
|
|
|
*也可使用DBServerProvider.手动获取数据库相关信息
|
|
|
|
|
|
*用户信息、权限、角色等使用UserContext.Current操作
|
2025-09-26 13:35:45 +08:00
|
|
|
|
*HR_DeptSyncService对增、删、改查、导入、导出、审核业务代码扩展参照ServiceFunFilter
|
2025-09-25 14:37:10 +08:00
|
|
|
|
*/
|
|
|
|
|
|
using Microsoft.AspNetCore.Http;
|
2025-09-30 08:59:35 +08:00
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using VOL.Core.DBManager;
|
|
|
|
|
|
using VOL.Core.Extensions;
|
|
|
|
|
|
using VOL.DingTalk.Models.Biz;
|
|
|
|
|
|
using VOL.DingTalk.Services.Biz;
|
|
|
|
|
|
using VOL.Entity.DomainModels.DeptShip.SystemDept;
|
2025-09-26 13:35:45 +08:00
|
|
|
|
using VOL.HR.IRepositories;
|
2025-09-30 08:59:35 +08:00
|
|
|
|
using VOL.YSErp.Models.Biz;
|
|
|
|
|
|
using VOL.YSErp.Services.Biz;
|
2025-09-25 14:37:10 +08:00
|
|
|
|
|
2025-09-26 13:35:45 +08:00
|
|
|
|
namespace VOL.HR.Services
|
2025-09-25 14:37:10 +08:00
|
|
|
|
{
|
2025-09-26 13:35:45 +08:00
|
|
|
|
public partial class HR_DeptSyncService
|
2025-09-25 14:37:10 +08:00
|
|
|
|
{
|
|
|
|
|
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
2025-09-26 13:35:45 +08:00
|
|
|
|
private readonly IHR_DeptSyncRepository _repository;//访问数据库
|
2025-09-30 08:59:35 +08:00
|
|
|
|
private readonly IHR_DeptShipRepository _shipRepository;//访问数据库
|
|
|
|
|
|
private readonly DingTalkService _dingTalkService;
|
|
|
|
|
|
private readonly YSERPService _ysService;
|
|
|
|
|
|
[ActivatorUtilitiesConstructor]
|
|
|
|
|
|
private Core.CacheManager.ICacheService _cacheService;
|
2025-09-25 14:37:10 +08:00
|
|
|
|
|
|
|
|
|
|
[ActivatorUtilitiesConstructor]
|
2025-09-26 13:35:45 +08:00
|
|
|
|
public HR_DeptSyncService(
|
|
|
|
|
|
IHR_DeptSyncRepository dbRepository,
|
2025-09-30 08:59:35 +08:00
|
|
|
|
IHR_DeptShipRepository shipDbRepository,
|
|
|
|
|
|
IHttpContextAccessor httpContextAccessor,
|
|
|
|
|
|
Core.CacheManager.ICacheService cacheService
|
2025-09-25 14:37:10 +08:00
|
|
|
|
)
|
|
|
|
|
|
: base(dbRepository)
|
|
|
|
|
|
{
|
|
|
|
|
|
_httpContextAccessor = httpContextAccessor;
|
|
|
|
|
|
_repository = dbRepository;
|
2025-09-30 08:59:35 +08:00
|
|
|
|
_shipRepository = shipDbRepository;
|
2025-09-25 14:37:10 +08:00
|
|
|
|
//多租户会用到这init代码,其他情况可以不用
|
|
|
|
|
|
//base.Init(dbRepository);
|
2025-09-30 08:59:35 +08:00
|
|
|
|
|
|
|
|
|
|
_cacheService = cacheService;
|
|
|
|
|
|
|
|
|
|
|
|
_dingTalkService = new DingTalkService(new DingTalk.Models.SystemToken(), new DingTalk.Models.DingTalkConfig());
|
|
|
|
|
|
_ysService = new YSERPService(new YSErp.Models.SystemToken(), new YSErp.Models.YSConfig());
|
2025-09-25 14:37:10 +08:00
|
|
|
|
}
|
2025-09-30 08:59:35 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async Task CacheYSDepartments()
|
|
|
|
|
|
{
|
|
|
|
|
|
var ysDepts = await _ysService.GetAllDepartmentsAsync();
|
|
|
|
|
|
if(ysDepts != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_cacheService.Remove("YS_DEPT_CACHE");
|
|
|
|
|
|
_cacheService.Add("YS_DEPT_CACHE", JsonConvert.SerializeObject(ysDepts), 60 * 60);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var db = DBServerProvider.SqlSugarClient;
|
|
|
|
|
|
|
|
|
|
|
|
// 自动建表
|
|
|
|
|
|
db.CodeFirst.InitTables<HR_YSDept>();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
db.BeginTran();
|
|
|
|
|
|
// 通过缓存直接反序列化转换对象
|
|
|
|
|
|
var deptsData = _cacheService.Get<List<HR_YSDept>>("YS_DEPT_CACHE");
|
|
|
|
|
|
|
|
|
|
|
|
// 清空再插入
|
|
|
|
|
|
db.DbMaintenance.TruncateTable<HR_YSDept>();
|
|
|
|
|
|
db.Fastest<HR_YSDept>().BulkCopy(deptsData);
|
|
|
|
|
|
db.CommitTran();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
db.RollbackTran();
|
|
|
|
|
|
throw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async Task CacheDingTalkDepartments()
|
|
|
|
|
|
{
|
|
|
|
|
|
var depts = await _dingTalkService.GetSubDepartmentsAsync();
|
|
|
|
|
|
if (depts != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_cacheService.Remove("DINGTALK_DEPT_CACHE");
|
|
|
|
|
|
_cacheService.Add("DINGTALK_DEPT_CACHE", JsonConvert.SerializeObject(depts), 60 * 60);
|
|
|
|
|
|
|
|
|
|
|
|
var db = DBServerProvider.SqlSugarClient;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 自动建表
|
|
|
|
|
|
db.CodeFirst.InitTables<HR_DingTalkDept>();
|
|
|
|
|
|
|
|
|
|
|
|
db.BeginTran();
|
|
|
|
|
|
// 通过缓存直接反序列化转换对象
|
|
|
|
|
|
var deptsData = _cacheService.Get<List<HR_DingTalkDept>>("DINGTALK_DEPT_CACHE");
|
|
|
|
|
|
|
|
|
|
|
|
// 清空再插入
|
|
|
|
|
|
db.DbMaintenance.TruncateTable<HR_DingTalkDept>();
|
|
|
|
|
|
db.Fastest<HR_DingTalkDept>().BulkCopy(deptsData);
|
|
|
|
|
|
db.CommitTran();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
db.RollbackTran();
|
|
|
|
|
|
throw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
public async Task CreateDingTalkDept(DingTalkDepartment dept)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task SyncYSERPDeptToDingTalk()
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
var ysDepts = await _ysService.GetAllDepartmentsAsync();
|
|
|
|
|
|
//var depts = await _dingTalkService.GetSubDepartmentsAsync();
|
|
|
|
|
|
//var deptShip = _shipRepository.Find(i => i.IsRoot ?? false);
|
|
|
|
|
|
if(ysDepts.Count != 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
var rootDept = _shipRepository.Find(i => i.IsRoot ?? false).FirstOrDefault();
|
|
|
|
|
|
BuildYSDeptTree(ysDepts).Where(i => i.id == rootDept.YSDeptId).ToList().ForEach(CreateOrUpdateDingTalkDept);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void CreateOrUpdateDingTalkDept(YSERPDepartment ysDept)
|
|
|
|
|
|
{
|
|
|
|
|
|
var deptShip = _shipRepository.Find(i => i.YSDeptId == ysDept.id).FirstOrDefault();
|
|
|
|
|
|
if (deptShip != null && (!deptShip.IsRoot ?? true))
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(ysDept.parentid))
|
|
|
|
|
|
{
|
|
|
|
|
|
var rootDept = _shipRepository.Find(i => i.IsRoot ?? false).FirstOrDefault();
|
|
|
|
|
|
var pId = int.Parse(rootDept?.DingTalkDeptId ?? "1");
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
_dingTalkService.CreateDingTalkDept(new DingTalkDepartment
|
|
|
|
|
|
{
|
|
|
|
|
|
parent_id = pId,
|
|
|
|
|
|
name = ysDept.name
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 将扁平的 YSERP 部门列表转换为树形结构
|
|
|
|
|
|
/// 规则:
|
|
|
|
|
|
/// - parentid 为 null/empty/"0" 或者父节点在列表中不存在时,视为根节点
|
|
|
|
|
|
/// - 避免将节点添加为自己的子节点
|
|
|
|
|
|
/// - 会初始化每个节点的 subDepts 列表并按 displayorder 排序(然后按 name 作为次级排序)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="flatList">扁平部门列表</param>
|
|
|
|
|
|
/// <returns>树形结构的根节点列表</returns>
|
|
|
|
|
|
private List<YSERPDepartment> BuildYSDeptTree(List<YSERPDepartment> flatList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (flatList == null || flatList.Count == 0)
|
|
|
|
|
|
return new List<YSERPDepartment>();
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化 subDepts,防止 null 引发 NRE
|
|
|
|
|
|
foreach (var d in flatList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (d.subDepts == null)
|
|
|
|
|
|
d.subDepts = new List<YSERPDepartment>();
|
|
|
|
|
|
else
|
|
|
|
|
|
d.subDepts.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 建立 id -> node 的字典(只包含有 id 的节点)
|
|
|
|
|
|
var dict = flatList
|
|
|
|
|
|
.Where(d => !string.IsNullOrEmpty(d.id))
|
|
|
|
|
|
.GroupBy(d => d.id) // 防止重复 id 导致异常,取第一个
|
|
|
|
|
|
.ToDictionary(g => g.Key, g => g.First());
|
|
|
|
|
|
|
|
|
|
|
|
var roots = new List<YSERPDepartment>();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var node in flatList)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 视为根的条件:parentid 为空、等于 "0"、或父节点不存在、或者 parentid 与自身 id 相同(防止自引用)
|
|
|
|
|
|
if (string.IsNullOrEmpty(node.parentid) || node.parentid == "0" ||
|
|
|
|
|
|
!dict.ContainsKey(node.parentid) || node.parentid == node.id)
|
|
|
|
|
|
{
|
|
|
|
|
|
roots.Add(node);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 将当前节点添加到父节点的子集合
|
|
|
|
|
|
if (dict.TryGetValue(node.parentid, out var parent))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (parent.subDepts == null)
|
|
|
|
|
|
parent.subDepts = new List<YSERPDepartment>();
|
|
|
|
|
|
parent.subDepts.Add(node);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 找不到父节点视为根
|
|
|
|
|
|
roots.Add(node);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 递归排序子节点(按 displayorder,再按 name)
|
|
|
|
|
|
void SortRecursively(List<YSERPDepartment> list)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (list == null || list.Count == 0) return;
|
|
|
|
|
|
list.Sort((a, b) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var c = a.displayorder.CompareTo(b.displayorder);
|
|
|
|
|
|
if (c == 0) return string.Compare(a.name, b.name, StringComparison.Ordinal);
|
|
|
|
|
|
return c;
|
|
|
|
|
|
});
|
|
|
|
|
|
foreach (var item in list)
|
|
|
|
|
|
{
|
|
|
|
|
|
SortRecursively(item.subDepts);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SortRecursively(roots);
|
|
|
|
|
|
|
|
|
|
|
|
return roots;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2025-09-25 14:37:10 +08:00
|
|
|
|
}
|