自動化運維-記自動添加客戶負責人的工作開發

来源:https://www.cnblogs.com/wenqingluomo/archive/2020/05/14/12889267.html
-Advertisement-
Play Games

公司自動開始用釘釘後,企業的相關信息化軟體開始使用釘釘上的應用程式。與銷售公司相關的就是CRM系統。 CRM系統中客戶是私人的,如果想多個人同時負責,需要添加客戶負責人。由於公司的特殊性質,客戶特別多,經常會有人要求增加客戶負責人。每天都有幾個小時在做這樣的工作。釘釘消息、添加負責人,太繁瑣了。 學 ...


公司自動開始用釘釘後,企業的相關信息化軟體開始使用釘釘上的應用程式。與銷售公司相關的就是CRM系統。

       CRM系統中客戶是私人的,如果想多個人同時負責,需要添加客戶負責人。由於公司的特殊性質,客戶特別多,經常會有人要求增加客戶負責人。每天都有幾個小時在做這樣的工作。釘釘消息、添加負責人,太繁瑣了。

       學以致用,開發個工具自動化處理任務。

       閑話少說,開乾。

 

設計思路

  1. 確認CRM是否有自動添加客戶負責人的介面
  2. 確認釘釘的審批介面是否可以使用
  3. 確認介面數據都能拿到後,後臺寫個服務即可。

CRM系統分配用戶

1、採集客戶信息

分配客戶負責人是通過客戶的dataid來處理的,首先需要採集客戶信息。幸好以前有定時服務採集客戶信息。跳過

2、分配客戶負責人介面

     客戶負責人分配才是這次的自動化處理的關鍵,通過查看CRM的API介面。可以實現,按照下麵的代碼執行即可。

public static void ReqDistributionCustomer(string datatids,string distributionUserIds, string userid, string corpid, string token, Action<string> SuccessCallback = null, Action<string> FailCallback = null)
        {
            string url = "http://127.0.0.1/pro/v1/api/customer/distribution";
            StringBuilder data = new StringBuilder();   data.Append($@"{{""corpid"":""{corpid}"",""dataIdList"":[{datatids}],""distributionUserIds"":[""{distributionUserIds}""],""subBusinessType"":101,""userId"":""{userid}""}}");
            string sign = Common.sha256($"{data.ToString()}{token}").ToLower();
            HttpWebRequest req = WebRequest.Create(url) as HttpWebRequest;
            req.Method = "POST";
            req.KeepAlive = true;
            req.ContentType = "application/json";
            req.Headers.Add("sign", sign);
            req.ServicePoint.ConnectionLimit = int.MaxValue;
            req.ServicePoint.Expect100Continue = false;
            req.Credentials = System.Net.CredentialCache.DefaultCredentials;

            byte[] buffer = Encoding.UTF8.GetBytes(data.ToString());
            using (Stream reqStream = req.GetRequestStream())
            {
                reqStream.Write(buffer, 0, buffer.Length);
            }
            req.BeginGetResponse(new AsyncCallback(RspDistributionCustomer), new object[] { req, datatids, userid, corpid, token, distributionUserIds, SuccessCallback, FailCallback });
        }

        private static void RspDistributionCustomer(IAsyncResult result)
        {
            object[] parms = result.AsyncState as object[];
            string datatids = parms[1].ToString();
            string userid = parms[2].ToString();
            string corpid = parms[3].ToString();
            string token = parms[4].ToString();
            string distributionUserIds = parms[5].ToString();
            HttpWebRequest req = parms[0] as HttpWebRequest;
            Action<string> SuccessCallback = parms[6] as Action<string>;
            Action<string> FailCallback = parms[7] as Action<string>;
            using (HttpWebResponse rsp = req.EndGetResponse(result) as HttpWebResponse)
            {
                using (StreamReader reader = new StreamReader(rsp.GetResponseStream()))
                {
                    string msg = "";
                    msg = reader.ReadToEnd();
                    var jsondata = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(msg);
                    if (!(bool)(jsondata.success))
                    {
                        if (FailCallback != null)
                            FailCallback(datatids);
                    }
                    else {
                        if (SuccessCallback != null)
                            SuccessCallback(datatids);
                    }
                }
            }
        }

獲取釘釘的審批信息

  1. 在釘釘上創建新審批,根據要求設置表單格式,支持多個客戶同時添加相同的負責人。
  2. 為了防止隨意添加客戶負責人 增加了欄位任意當前客戶負責人,可以在系統中進行校驗。 或者是 定期對所有客戶的負責人進行校驗,如果沒有任何跟進、銷售機會、合同信息可以主動刪除該客戶負責人。
  3. 關於附件,其實最好的辦法是把客戶放到附件裡面,但是看了一下開放平臺,後臺開發文檔,沒有辦法獲取審批中的附件信息。【難過】,只好允許用戶直接錄入多個客戶了

 

 

獲取釘釘的審批記錄

釘釘要求每次獲取只能獲取20條記錄,如果想獲取更多只能翻頁獲取。

其實關於釘釘的開放平臺,講解的還是很詳細的,根據要求傳參數即可。釘釘現在都是Java開發了,.NET還得自己寫。

對於多年的.NET程式員大叔來說,這都不是事,輕鬆搞定.

public static void ReqDingProcess(string token, DateTime dt, Action<string> SuccessCallback, Action<string> FailCallback,int cursor=0)
        {
            string url = $"https://oapi.dingtalk.com/topapi/processinstance/listids?access_token={token}";
            StringBuilder data = new StringBuilder();
            data.Append($@"{{""process_code"":""{FzrProcessCode}"",""start_time"":{Common.ConvertDateTimeLong(dt)},""size"":20,""cursor"":{cursor}}}");
            HttpWebRequest req = WebRequest.Create(url) as HttpWebRequest;
            req.Method = "POST";
            req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36";
            req.Accept = "application/json, text/plain, */*";
            req.KeepAlive = true;
            req.ContentType = "application/json;charset=UTF-8";
            req.Headers.Add("X-Requested-With", "XMLHttpRequest");
            req.ServicePoint.ConnectionLimit = int.MaxValue;
            req.ServicePoint.Expect100Continue = false;
            req.Credentials = System.Net.CredentialCache.DefaultCredentials;

            byte[] buffer = Encoding.UTF8.GetBytes(data.ToString());
            using (Stream reqStream = req.GetRequestStream())
            {
                reqStream.Write(buffer, 0, buffer.Length);
            }
            req.BeginGetResponse(new AsyncCallback(RspDingProcess), new object[] { req, token, dt, SuccessCallback, FailCallback });
        }

        public static void RspDingProcess(IAsyncResult result)
        {
            object[] parms = result.AsyncState as object[];
            string token = parms[1].ToString();
            DateTime dt = DateTime.Parse(parms[2].ToString());
            HttpWebRequest req = parms[0] as HttpWebRequest;
            Action<string> SuccessCallback = parms[3] as Action<string>;
            Action<string> FailCallback = parms[4] as Action<string>;
            using (HttpWebResponse rsp = req.EndGetResponse(result) as HttpWebResponse)
            {
                using (StreamReader reader = new StreamReader(rsp.GetResponseStream()))
                {
                    string msg = "";
                    msg = reader.ReadToEnd();
                    var jsondata = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(msg);
                    if (jsondata.result.next_cursor != null) {
                        int nextcursor =int.Parse( jsondata.result.next_cursor.ToString());
                        ReqDingProcess(token, dt, SuccessCallback, FailCallback, nextcursor);
                    }
                    var rows = jsondata.result.list;
                    foreach (var row in rows)
                    {
                        queueFzrProcess.Enqueue(row.ToString());
                    }
                }
            }

        }


        public static void ReqDingProcessInfo(string token,string procid, Action<string> SuccessCallback, Action<string> FailCallback)
        {
            string url = $"https://oapi.dingtalk.com/topapi/processinstance/get?access_token={token}";
            StringBuilder data = new StringBuilder();
            data.Append($@"{{""process_instance_id"":""{procid}""}}");
            HttpWebRequest req = WebRequest.Create(url) as HttpWebRequest;
            req.Method = "POST";
            req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36";
            req.Accept = "application/json, text/plain, */*";
            req.KeepAlive = true;
            req.ContentType = "application/json;charset=UTF-8";
            req.Headers.Add("X-Requested-With", "XMLHttpRequest");
            req.ServicePoint.ConnectionLimit = int.MaxValue;
            req.ServicePoint.Expect100Continue = false;
            req.Credentials = System.Net.CredentialCache.DefaultCredentials;

            byte[] buffer = Encoding.UTF8.GetBytes(data.ToString());
            using (Stream reqStream = req.GetRequestStream())
            {
                reqStream.Write(buffer, 0, buffer.Length);
            }
            req.BeginGetResponse(new AsyncCallback(RspDingProcessInfo), new object[] { req, token, procid, SuccessCallback, FailCallback });
        }

        public static void RspDingProcessInfo(IAsyncResult result)
        {
            object[] parms = result.AsyncState as object[];
            string token = parms[1].ToString();
            string procid = parms[2].ToString();
            HttpWebRequest req = parms[0] as HttpWebRequest;
            Action<string> SuccessCallback = parms[3] as Action<string>;
            Action<string> FailCallback = parms[4] as Action<string>;
            using (HttpWebResponse rsp = req.EndGetResponse(result) as HttpWebResponse)
            {
                using (StreamReader reader = new StreamReader(rsp.GetResponseStream()))
                {
                    string msg = "";
                    msg = reader.ReadToEnd();
                    var jsondata = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(msg);
                    string businessid = jsondata.process_instance.business_id.ToString();
                    string customers = "";
                    string fzrNew = "";
                    StringBuilder sbFzrSource = new StringBuilder();
                    var fields = jsondata.process_instance.form_component_values;
                    foreach (var field in fields) {
                        switch (field.name.ToString()) {
                            case "客戶名稱": {
                                    customers = field.value.ToString().Trim();
                                    break;
                                }
                            case "需添加的負責人": {
                                    string userstr= field.ext_value.ToString();
                                    var jsonuser= Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(userstr);
                                    foreach (var u in jsonuser)
                                    {
                                        fzrNew = u.emplId.ToString().Trim();
                                    }
                                    break;
                                }
                            case "選擇任意當前客戶負責人": {
                                    string userstr = field.ext_value.ToString();
                                    var jsonuser = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(userstr);
                                    foreach (var u in jsonuser) {
                                        sbFzrSource.Append($",{u.emplId.ToString().Trim()}");
                                    }
                                    break;
                                }
                        }
                    }
                    if (sbFzrSource.Length > 0)
                        sbFzrSource.Remove(0, 1);
                    string[] lstcustomer = customers.Split(new string[] { "\n", ",", ",", "|" }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string str in lstcustomer)
                    {
                        ConcurrentDictionary<string, string> dic = new ConcurrentDictionary<string, string>();
                        dic.AddOrUpdate("businessid", businessid, (k, v) => businessid);
                        dic.AddOrUpdate("customer", str, (k, v) => str);
                        dic.AddOrUpdate("fzr", fzrNew, (k, v) => fzrNew);
                        dic.AddOrUpdate("fzrSource", sbFzrSource.ToString(), (k, v) => sbFzrSource.ToString());
                        dic.AddOrUpdate("procid", procid,(k,v)=>procid);
                        queueProcs.Enqueue(dic);
                    }
                }
            }

        }

定時任務

5分鐘抓取一次審批。

根據抓到的最新的審批記錄,查詢每個審批記錄的詳情,獲取相關的客戶信息、負責人信息。

審批通過後把結果寫入資料庫,已經抓取過的數據,不再進行處理

問題:由於釘釘和CRM系統的介面處理有一定的時間差異,一般來說釘釘的系統比較快,由於CRM系統沒有處理完,釘釘已經把業務處理完了,所以判斷資料庫那可能會出現重覆。處理過的審批最好放到緩存中才完美。

 

#region 獲取負責人流程信息
            Task.Factory.StartNew(() =>
            {
                DateTime dt = DateTime.Parse(DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd"));
                //5分鐘一次
                while (true)
                {
                    if (string.IsNullOrWhiteSpace(Business.DingAccessToken))
                    {
                        Thread.Sleep(1000);
                        continue;
                    }
                    Business.ReqDingProcess(Business.DingAccessToken, dt,
                        new Action<string>(str => { }),
                        new Action<string>(str => { })
                        );
                    dt = DateTime.Now;
                    Thread.Sleep(1000 * 60 * 5);
                }
            });
            //獲取流程信息
            Task.Factory.StartNew(() =>
            {

                string logpath = $"{AppDomain.CurrentDomain.BaseDirectory}/logs/DingProcess";
                if (!Directory.Exists(logpath))
                    Directory.CreateDirectory(logpath);

                while (true)
                {
                    if (string.IsNullOrWhiteSpace(Business.DingAccessToken))
                    {
                        Thread.Sleep(1000);
                        continue;
                    }
                    if (Business.queueFzrProcess.IsEmpty)
                    {
                        Thread.Sleep(1000);
                        continue;
                    }
                    Business.queueFzrProcess.TryDequeue(out string procid);
                    if (string.IsNullOrWhiteSpace(procid))
                        continue;
                    //如果該條目已經處理 則不再處理
                    var procids= DbAccess.Query($"select 1 from DingProc_CustomerFZR where procid='{procid}'");
                    if (procids.Any())
                        continue;
                    Business.ReqDingProcessInfo(Business.DingAccessToken, procid,
                        new Action<string>(str => { }),
                        new Action<string>(str => { })
                        );
                    Thread.Sleep(100);
                }
            });
            //根據客戶名稱查詢客戶ID 並記錄客戶信息到資料庫
            Task.Factory.StartNew(() => {
                Dictionary<string, ConcurrentDictionary<string, string>> dicTemp = new Dictionary<string, ConcurrentDictionary<string, string>>();
                while (true)
                {
                    if (Business.XbbModel == null)
                    {
                        Thread.Sleep(1000);
                        continue;
                    }
                    if (Business.queueProcs.IsEmpty) {
                        Thread.Sleep(1000);
                        continue;
                    }
                    Business.queueProcs.TryDequeue(out ConcurrentDictionary<string, string> dic);
                    if (dic == null) {
                        Thread.Sleep(1000);
                        continue;
                    }
                    //查詢資料庫中客戶是否存在
                    var custid= DbAccess.Query($"select dataId from Customer{DateTime.Now.ToString("yyyyMMdd")} where text_1='{dic["customer"]}'");
                    if (!custid.Any())
                    {
                        dic.AddOrUpdate("result", "客戶名稱不存在",(k,v)=>v);
                        Business.queueProcsResult.Enqueue(dic);
                        //客戶名稱不存在,保存到資料庫等待處理結果
                        Thread.Sleep(1000);
                        continue;
                    }
                    
                    string dataid = (custid.FirstOrDefault()).dataId.ToString();
                    if (dicTemp.ContainsKey(dataid)) {
                        Business.queueProcs.Enqueue(dic);
                        Thread.Sleep(10000);
                        continue;
                    }
                    dicTemp.Add(dataid, dic);
                    Business.ReqDistributionCustomer(dataid, dic["fzr"], Business.XbbModel.UserID, Business.XbbModel.CorpId, Business.XbbModel.token,
                        new Action<string>(success => {
                            dicTemp[success].AddOrUpdate("result", "成功", (k, v) => v);
                            Business.queueProcsResult.Enqueue(dicTemp[success]);
                            dicTemp.Remove(success);
                        }),
                        new Action<string>(fail => {
                            dicTemp[fail].AddOrUpdate("result", "失敗", (k, v) => v);
                            Business.queueProcsResult.Enqueue(dicTemp[fail]);
                            dicTemp.Remove(fail);
                        })
                        );
                }
            });


            Task.Factory.StartNew(() =>
            {
                List<Dictionary<string, string>> lstdic = new List<Dictionary<string, string>>();
                while (true) {

                    if (Business.queueProcsResult.IsEmpty) {
                        Thread.Sleep(1000);
                        continue;
                    }
                    while (!Business.queueProcsResult.IsEmpty) {
                        if (lstdic.Count > 50)
                            break;
                        Business.queueProcsResult.TryDequeue(out ConcurrentDictionary<string, string> dic);
                        if (dic == null)
                            continue;
                        Dictionary<string, string> dicTemp = new Dictionary<string, string>();
                        foreach (var kv in dic)
                            dicTemp.Add(kv.Key, kv.Value);
                        lstdic.Add(dicTemp);
                    }
                    DbAccess.AddTran(lstdic, "DingProc_CustomerFZR", null);

                }
            });
            #endregion

歡迎大家點評

學以致用,解放勞動力,把更多時光用在更美好的生活里。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • SpringCLoud 總覽 [參考鏈接]: https://mp.weixin.qq.com/s/ShPlCT41Z_M0iElQTZZ07A 服務註冊/發現&註冊中心 1、Eureka 服務註冊(服務提供者)/發現(服務調用者)&註冊中心(服務中介) 服務註冊: 當 客戶端向 註冊時,它提供自身 ...
  • 在一個二維數組中(每個一維數組的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。public static void main(String [] args){ int value = 5 ...
  • golang語言特性 1. 垃圾回收 a. 記憶體自動回收,再也不需要開發人員管理記憶體 b. 開發人員專註業務實現,降低了心智負擔 c. 只需要new分配記憶體,不需要釋放 2. 天然併發 a . 從語言層面支持併發,非常簡單 b. goroute,輕量級線程,創建成千上萬個goroute成為可能 c. ...
  • .net core 3.1中將session持久化到redis,從core1.0開始,2.0/2.1,3.0/3.1每次搭建開發框架都因為session的問題搞的頭大,次次踩坑,花了半天時間再次捋了一遍,發出來如果大家有跟我一樣,希望能幫到你,另外如果有幫到你,記得點贊哈!!! 1.需引入的庫文件 ...
  • 1、前言 文件配置提供程式預設的給我們提供了ini、json、Xml等。都是讀取不同格式的文件。文件配置提供程式支持文件可尋、必選、文件變更的監視。 2、讀取配置文件 主要運用的包:需要Ini、xml的添加相關包就行。 這次依然使用 Core3.1 WebApi項目,在啟動程式介面裡面讀取json配 ...
  • 最近公司開發的項目,分開的版本太多,但是又不是聯網版本,也就沒有登錄,不能通過用戶登錄來控制相關功能。 比如A局有個改動要求,B局有個改動要求,但是,又是同時修改的一個M文件,原來的做法是,把M文件複製一下,分別給A局和B局修改,但是這種方法有個很大的問題,就是修改公共的部分代碼,就需要Double ...
  • 介紹一套用C#寫的WindowForm進銷存管理系統,它有一個特點,也非常有意義。 特點: 系統涵括進銷存的各種業務功能,對想瞭解進銷存的開發人員有一定引導作用,其次是UI規劃相對整潔。 意義: 這是我在大學時期,使用了一個暑假假期的時間研發出來的,當時我還在研究C++,初聞C#誕生,便嘗試用它開發 ...
  • <asp:Repeater ID="rptList" runat="server" OnItemDataBound="Getdata"> <HeaderTemplate> <table width="100%" border="0" cellspacing="0" cellpadding="0" c ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...