將本地公司部門信息更新到微信企業號通信錄(可改進爲按期更新)node
保存在本地數據庫的公司部門信息,採用相似以下格式保存:算法
UCName | UCID | ParentUCID |
---|---|---|
A | 1001000 | |
B | 1002000 | |
A1 | 1001100 | 1001000 |
A2 | 1001200 | 1001000 |
A11 | 1001110 | 1001100 |
...... |
因爲UCID爲字符串格式,而通信錄保存的公司部門ID爲int類型,因此上傳到企業號通信錄的公司部門信息會造成新的ID。這就造成了同一部門在本地、通信錄各有各的ID、父ID。
數據庫
爲保證部門結構關係同步準確,對部門數據進行樹形結構化,因爲部門信息,部門名可能存在同名,但同一部門下不存在同名的子部門,因此對各節點進行部門名、父節點關係進行匹對,以肯定兩棵部門樹最終一致(ID和父ID不一樣)。
同時創建一「公司-通信錄」部門映射表。微信
原始本地『公司部門樹』以下:ui
上傳後的『通信錄部門樹』以下:
spa
『公司—通信錄部門映射表』數據:3d
UCName | DPID | ParentDPID | UCID | ParentUCID |
---|---|---|---|---|
A | 2 | 1 | 1001000 | |
B | 3 | 1 | 1002000 | |
A1 | 4 | 2 | 1001100 | 1001000 |
A2 | 5 | 2 | 1001200 | 1001000 |
A11 | 6 | 4 | 1001110 | 1001100 |
樹結構:orm
【部門數據變化】
當本地公司部門信息改變後,本地部門樹以下:blog
新增部門數(橘紅色):4
更新部門數(橘黃色):1(含1子部門)
因爲有新部門父節點也是新增部門,爲保證父子部門關係正確,對新增部門的同步化採用:先添加新增部門,再肯定父部門編號。
將公司部門表與映射表進行匹對,向映射表添加新的部門信息,造成以下樹結構:ci
這裏看到在A部門下的A1部門還未同步,因而更新A1部門父節點信息,造成最終樹結構:
至此,同步好通信錄部門樹。
主程序:
class DeptTree_Test { static void Main(string[] args) { TreeNode rootUC = new TreeNode() { NName = "組織", NID = "1", ParentNID = "" }; TreeNode rootDept = new TreeNode() { NName = "組織", NID = "1", ParentNID = "" }; DeptTree_Test p = new DeptTree_Test(); p.InitUC(); // 模擬從本地數據庫讀取公司部門信息 Console.WriteLine("===========公司部門==========="); p.PrintTree(rootUC, LocalUC); // 生成打印出公司部門樹 p.InitDept(); // 模擬從通信錄下載部門信息 Console.WriteLine("===========通信錄==========="); p.PrintTree(rootDept, Dept); // 生成打印出通信錄部門樹 rootDept.ChildrenNode.Clear(); p.MakeMap(); // 模擬讀取『公司-通信錄』部門映射表 Console.WriteLine("===========映射表==========="); p.PrintTree(rootDept, Map); // 生成打印出映射表部門樹 rootUC.ChildrenNode.Clear(); p.ChangeUC(); // 模擬公司部門信息更新 Console.WriteLine("===========公司部門改變==========="); p.PrintTree(rootUC, LocalUC); // 生成打印出新的公司部門樹 Console.WriteLine("===========映射表增量變化==========="); var ltNewOU = p.treeManager.Subtract(rootUC, rootDept, TreeManager.MatchTag.NName); // 獲取本地公司部門與映射表部門差別部門信息 List<DataRow> lToFixNewDP = new List<DataRow>(); // 先新增 foreach (var ou in ltNewOU) { //Console.WriteLine("{?} - {?},{?}({?})".Format_Ext(ou.NName, ou.NID, ou.ParentNID, JsonConvert.SerializeObject(ou.ExtraInfo))); if (ou.ExtraInfo["Extra_DiffType"].ToString() == "MISSING") // 映射表中缺失的公司新增部門 { DataRow nDr = Map.NewRow(); nDr[0] = ou.NName; nDr[1] = p.autoIncreaseID++; // 模擬預新增通信錄部門ID nDr[3] = ou.NID; nDr[4] = ou.ParentNID; Map.Rows.Add(nDr); lToFixNewDP.Add(nDr); } } // 而後肯定父節點 foreach (DataRow mr in Map.Rows) { if (!lToFixNewDP.Exists(_d => _d[1].ToString() == mr[1].ToString())) continue; foreach (DataRow r in Map.Rows) { if (r["Extra_UCID"].ToString() == mr[4].ToString()) { mr[2] = r[1]; } } // 這裏能夠調用API添加到企業號(或先臨時保存新增部門信息,後面統一更新) } rootDept.ChildrenNode.Clear(); p.PrintTree(rootDept, Map); // 打印出增量變化後的映射表部門樹 Console.WriteLine("===========映射表父節點變化==========="); foreach (var ou in ltNewOU) { if (ou.ExtraInfo["Extra_DiffType"].ToString() == "DIFFERENT") { foreach (DataRow dr in Map.Rows) { if (dr["Extra_UCID"].ToString() == ou.NID) { foreach (DataRow pdr in Map.Rows) { if (pdr["Extra_UCID"].ToString() == ou.ParentNID) { dr[2] = pdr[1]; // 這裏能夠調用API更新到企業號(或先臨時保存變化的部門信息,後面統一更新) } } } } } } rootDept.ChildrenNode.Clear(); p.PrintTree(rootDept, Map); // 打印出父節點調節後的映射表部門樹 //// 移除通信錄上多餘的部門 //var ltToRmvDept = p.treeManager.Subtract(rootDept, rootUC, TreeManager.MatchTag.NName); Console.WriteLine("Done"); Console.ReadKey(); } TreeManager treeManager = new TreeManager(); public static DataTable Dept = new DataTable(); public static DataTable LocalUC = new DataTable(); public static DataTable Map = new DataTable(); int autoIncreaseID = 7; void InitUC() { LocalUC.Columns.Add("NName"); LocalUC.Columns.Add("NID"); LocalUC.Columns.Add("ParentNID"); DataRow nDr = LocalUC.NewRow(); nDr[0] = "A"; nDr[1] = "1001000"; nDr[2] = ""; LocalUC.Rows.Add(nDr); nDr = LocalUC.NewRow(); nDr[0] = "B"; nDr[1] = "1002000"; nDr[2] = ""; LocalUC.Rows.Add(nDr); nDr = LocalUC.NewRow(); nDr[0] = "A1"; nDr[1] = "1001100"; nDr[2] = "1001000"; LocalUC.Rows.Add(nDr); nDr = LocalUC.NewRow(); nDr[0] = "A2"; nDr[1] = "1001200"; nDr[2] = "1001000"; LocalUC.Rows.Add(nDr); nDr = LocalUC.NewRow(); nDr[0] = "A11"; nDr[1] = "1001110"; nDr[2] = "1001100"; LocalUC.Rows.Add(nDr); } void ChangeUC() { LocalUC.Rows[2][2] = "1002000"; DataRow nDr = LocalUC.NewRow(); nDr[0] = "A3"; nDr[1] = "1001300"; nDr[2] = "1001000"; LocalUC.Rows.Add(nDr); nDr = LocalUC.NewRow(); nDr[0] = "A21"; nDr[1] = "1001210"; nDr[2] = "1001200"; LocalUC.Rows.Add(nDr); nDr = LocalUC.NewRow(); nDr[0] = "A211"; nDr[1] = "1001211"; nDr[2] = "1001210"; LocalUC.Rows.Add(nDr); nDr = LocalUC.NewRow(); nDr[0] = "B1"; nDr[1] = "1002100"; nDr[2] = "1002000"; LocalUC.Rows.Add(nDr); } void InitDept() { Dept.Columns.Add("NName"); Dept.Columns.Add("NID"); Dept.Columns.Add("ParentNID"); DataRow nDr = Dept.NewRow(); nDr[0] = "A"; nDr[1] = "2"; nDr[2] = ""; Dept.Rows.Add(nDr); nDr = Dept.NewRow(); nDr[0] = "B"; nDr[1] = "3"; nDr[2] = ""; Dept.Rows.Add(nDr); nDr = Dept.NewRow(); nDr[0] = "A1"; nDr[1] = "4"; nDr[2] = "2"; Dept.Rows.Add(nDr); nDr = Dept.NewRow(); nDr[0] = "A2"; nDr[1] = "5"; nDr[2] = "2"; Dept.Rows.Add(nDr); nDr = Dept.NewRow(); nDr[0] = "A11"; nDr[1] = "6"; nDr[2] = "4"; Dept.Rows.Add(nDr); } void MakeMap() { Map = new DataTable(); Map.Columns.Add("NName"); Map.Columns.Add("NID"); Map.Columns.Add("ParentNID"); Map.Columns.Add("Extra_UCID"); Map.Columns.Add("Extra_ParentUCID"); foreach (DataRow dp in Dept.Rows) { DataRow nDr = Map.NewRow(); nDr[0] = dp[0]; nDr[1] = dp[1]; nDr[2] = dp[2]; foreach (DataRow uc in LocalUC.Rows) { if (uc[0] == dp[0]) { nDr[3] = uc[1]; nDr[4] = uc[2]; break; } } Map.Rows.Add(nDr); } } void PrintTree(TreeNode root, DataTable dt) { treeManager.BuildTree("", root, dt, 0); var lst = treeManager.Traverse(root); StringBuilder sb = new StringBuilder(); sb.AppendLine(lst[0].NName); for (int i = 1; i < lst.Count; i++) { var item = lst[i]; string str = ""; for (int j = 0; j <= item.DeepIndex; j++) { str += "\t"; } if (item.ExtraInfo.Count == 0) { sb.AppendLine(str + item.NName + "(" + item.NID + ")"); } else { sb.AppendLine(str + item.NName + "(" + item.NID + ":" + item.ExtraInfo.ElementAt(0).Value.ToString() + ")"); } } Console.WriteLine(sb.ToString()); } }
樹算法類:
public class TreeManager { public enum MatchTag { NID, NName } public class ExtraInfo { public string Key = ""; public object Value = ""; } public int TreeDeep = 0; public void BuildTree(string parentNID, TreeNode tn, DataTable dt, int dp) { if (dp > 100) throw new Exception("Too Deep!"); if (dp > TreeDeep) TreeDeep = dp; foreach (DataRow dr in dt.Rows) { if ((parentNID == null && dr["ParentNID"] == null) || (parentNID != null && dr["ParentNID"].ToString() == parentNID)) { var o = new TreeNode() { NID = dr["NID"].ToString().Trim(), NName = dr["NName"].ToString().Trim(), ParentNID = parentNID, DeepIndex = dp }; foreach (DataColumn dc in dt.Columns) { if (dc.ColumnName.StartsWith("Extra_")) { o.ExtraInfo.Add(dc.ColumnName, dr[dc.ColumnName]); } } o.ParentNode = tn; tn.ChildrenNode.Add(o); BuildTree(o.NID, o, dt, dp + 1); } } } public TreeNode FindInTree(TreeNode tnode, string nid, string nname, ExtraInfo extraInfo) { if (tnode == null) return null; TreeNode tn = null; if (tn == null && !string.IsNullOrEmpty(nid)) { if (tnode.NID == nid) tn = tnode; } if (tn == null && !string.IsNullOrEmpty(nname)) { if (tnode.NName == nname) tn = tnode; } if (tn == null && extraInfo != null) { foreach (string k in tnode.ExtraInfo.Keys) { if (k == extraInfo.Key && extraInfo.Value.ToString() == tnode.ExtraInfo[k].ToString()) { tn = tnode; return tn; } } } if (tn == null && tnode.ChildrenNode.Count > 0) { foreach (var item in tnode.ChildrenNode) { tn = FindInTree(item, nid, nname, extraInfo); if (tn != null) break; } } return tn; } /// <summary> /// 匹配樹結構 /// </summary> /// <param name="root1"></param> /// <param name="root2"></param> /// <param name="mt">根據節點ID/節點名稱進行匹對</param> /// <returns></returns> public List<TreeNode> Subtract(TreeNode root1, TreeNode root2, MatchTag mt = MatchTag.NID) { List<TreeNode> lTN = new List<TreeNode>(); var lPath1 = Traverse(root1); var lPath2 = Traverse(root2); bool pass = false; foreach (var item in lPath1) { pass = false; for (int i = 0; i < lPath2.Count; i++) { if (mt == MatchTag.NID) { if (lPath2[i].NID == item.NID) { if (((lPath2[i].ParentNode != null && item.ParentNode != null && lPath2[i].ParentNode.NID == item.ParentNode.NID) || lPath2[i].ParentNode == null && item.ParentNode == null)) { lPath2.RemoveAt(i--); pass = true; break; } item.ExtraInfo.Add("Extra_DiffType", "DIFFERENT"); } } else if (mt == MatchTag.NName) { if (lPath2[i].NName == item.NName) { if (((lPath2[i].ParentNode != null && item.ParentNode != null && lPath2[i].ParentNode.NName == item.ParentNode.NName) || lPath2[i].ParentNode == null && item.ParentNode == null)) { lPath2.RemoveAt(i--); pass = true; break; } item.ExtraInfo.Add("Extra_DiffType", "DIFFERENT"); } } } if (!pass) { if (!item.ExtraInfo.ContainsKey("Extra_DiffType")) item.ExtraInfo.Add("Extra_DiffType", "MISSING"); lTN.Add(item); } } return lTN; } /// <summary> /// 遍歷樹(縱向) /// </summary> /// <param name="root"></param> /// <returns></returns> public List<TreeNode> Traverse(TreeNode root) { List<TreeNode> lPath = new List<TreeNode>(); lPath.Add(root); if (root.ChildrenNode.Count > 0) { foreach (var item in root.ChildrenNode) { foreach (var n in Traverse(item)) { lPath.Add(n); } } } return lPath; } /// <summary> /// 遍歷樹(橫向) /// </summary> /// <param name="root"></param> /// <returns></returns> public List<TreeNode> TraverseByLayer(TreeNode root) { List<TreeNode> lPath = new List<TreeNode>(); lPath.Add(root); TreeNode tn = root; Queue<TreeNode> qToTraverseTN = new Queue<TreeNode>(); List<TreeNode> lToTraverseTN = new List<TreeNode>(); do { foreach (var item in tn.ChildrenNode) { lPath.Add(item); qToTraverseTN.Enqueue(item); } if (qToTraverseTN.Count == 0) break; tn = qToTraverseTN.Dequeue(); } while (true); return lPath; } } public class TreeNode { public int DeepIndex = 0; public string NID; public string NName; public string ParentNID; public Dictionary<string, object> ExtraInfo = new Dictionary<string, object>(); public TreeNode ParentNode = null; public List<TreeNode> ChildrenNode = new List<TreeNode>(); public int TotalChildrenNodeCount() { int childrenOUTotalCount = ChildrenNode.Count; foreach (var item in ChildrenNode) { int tc = item.TotalChildrenNodeCount(); childrenOUTotalCount += tc; } return childrenOUTotalCount; } }