【樹結構數據同步】公司部門—通信錄數據同步

  • 需求

將本地公司部門信息更新到微信企業號通信錄(可改進爲按期更新)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;
    }
}
相關文章
相關標籤/搜索