/* *所有关于HR_DeptSync类的业务代码应在此处编写 *可使用repository.调用常用方法,获取EF/Dapper等信息 *如果需要事务请使用repository.DbContextBeginTransaction *也可使用DBServerProvider.手动获取数据库相关信息 *用户信息、权限、角色等使用UserContext.Current操作 *HR_DeptSyncService对增、删、改查、导入、导出、审核业务代码扩展参照ServiceFunFilter */ using Microsoft.AspNetCore.Http; 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; using VOL.HR.IRepositories; using VOL.YSErp.Models.Biz; using VOL.YSErp.Services.Biz; namespace VOL.HR.Services { public partial class HR_DeptSyncService { private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHR_DeptSyncRepository _repository;//访问数据库 private readonly IHR_DeptShipRepository _shipRepository;//访问数据库 private readonly DingTalkService _dingTalkService; private readonly YSERPService _ysService; [ActivatorUtilitiesConstructor] private Core.CacheManager.ICacheService _cacheService; [ActivatorUtilitiesConstructor] public HR_DeptSyncService( IHR_DeptSyncRepository dbRepository, IHR_DeptShipRepository shipDbRepository, IHttpContextAccessor httpContextAccessor, Core.CacheManager.ICacheService cacheService ) : base(dbRepository) { _httpContextAccessor = httpContextAccessor; _repository = dbRepository; _shipRepository = shipDbRepository; //多租户会用到这init代码,其他情况可以不用 //base.Init(dbRepository); _cacheService = cacheService; _dingTalkService = new DingTalkService(new DingTalk.Models.SystemToken(), new DingTalk.Models.DingTalkConfig()); _ysService = new YSERPService(new YSErp.Models.SystemToken(), new YSErp.Models.YSConfig()); } 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(); try { db.BeginTran(); // 通过缓存直接反序列化转换对象 var deptsData = _cacheService.Get>("YS_DEPT_CACHE"); // 清空再插入 db.DbMaintenance.TruncateTable(); db.Fastest().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(); db.BeginTran(); // 通过缓存直接反序列化转换对象 var deptsData = _cacheService.Get>("DINGTALK_DEPT_CACHE"); // 清空再插入 db.DbMaintenance.TruncateTable(); db.Fastest().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; } } } } /// /// 将扁平的 YSERP 部门列表转换为树形结构 /// 规则: /// - parentid 为 null/empty/"0" 或者父节点在列表中不存在时,视为根节点 /// - 避免将节点添加为自己的子节点 /// - 会初始化每个节点的 subDepts 列表并按 displayorder 排序(然后按 name 作为次级排序) /// /// 扁平部门列表 /// 树形结构的根节点列表 private List BuildYSDeptTree(List flatList) { if (flatList == null || flatList.Count == 0) return new List(); // 初始化 subDepts,防止 null 引发 NRE foreach (var d in flatList) { if (d.subDepts == null) d.subDepts = new List(); 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(); 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(); parent.subDepts.Add(node); } else { // 找不到父节点视为根 roots.Add(node); } } } // 递归排序子节点(按 displayorder,再按 name) void SortRecursively(List 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; } } }