【树结构数据同步】公司部门—通讯录数据同步

  • 需求

将本地公司部门信息更新到微信企业号通讯录(可改进为定期更新)

 

  • 说明

保存在本地数据库的公司部门信息,采用类似如下格式保存:

UCNameUCIDParentUCID
A 1001000  
B 1002000  
A1 1001100 1001000
A2 1001200 1001000
A11 1001110 1001100
......

由于UCID为字符串格式,而通讯录保存的公司部门ID为int类型,所以上传到企业号通讯录的公司部门信息会形成新的ID。这就形成了同一部门在本地、通讯录各有各的ID、父ID。

  • 解决方案

为保证部门结构关系同步准确,对部门数据进行树形结构化,由于部门信息,部门名可能存在同名,但同一部门下不存在同名的子部门,所以对各节点进行部门名、父节点关系进行匹对,以确定两棵部门树最终一致(ID和父ID不同)。
同时建立一“公司-通讯录”部门映射表。


原始本地『公司部门树』如下:



上传后的『通讯录部门树』如下:

 

『公司—通讯录部门映射表』数据:

UCNameDPIDParentDPIDUCIDParentUCID
A 2 1 1001000  
B 3 1 1002000  
A1 4 2 1001100 1001000
A2 5 2 1001200 1001000
A11 6 4 1001110 1001100

树结构:

 

【部门数据变化】
当本地公司部门信息改变后,本地部门树如下:


新增部门数(橘红色):4
更新部门数(橘黄色):1(含1子部门)


由于有新部门父节点也是新增部门,为保证父子部门关系正确,对新增部门的同步化采用:先添加新增部门,再确定父部门编号。

将公司部门表与映射表进行匹对,向映射表添加新的部门信息,形成如下树结构:

 

这里看到在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;
    }
}

 

posted @ 2016-09-06 15:56  Glife  阅读(1634)  评论(0编辑  收藏  举报