After the company started to use nails automatically, the related information software of the enterprise began to use the application program on nails. Related to the sales company is the CRM system.
In CRM system, the customer is private. If you want more than one person to be responsible at the same time, you need to add the customer leader. Due to the special nature of the company and the large number of customers, it is often required to increase the number of customers in charge. Every day, there are several hours to do such work. It's too cumbersome to pin the message and add the person in charge.
Apply what you have learned and develop a tool to automate tasks.
Don't talk too much, just do it.
Design ideas
- Confirm whether CRM has an interface to automatically add customer owner
- Confirm whether the approval interface of nail can be used
- After confirming that all the interface data can be obtained, write a service in the background.
CRM system assigns users
1. Collect customer information
The allocation of customer principal is handled by the customer's dataid. First, the customer information needs to be collected. Fortunately, there was a regular service to collect customer information. skip
2. Assign customer owner interface
The key to this automation is to assign the customer leader. Check the API interface of CRM. It can be implemented by following the code below.
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); } } } }
Get approval information of nails
- Create a new approval on the pin, format the form as required, and support multiple customers to add the same person in charge at the same time.
- In order to prevent any current customer leader from being added at will, the field can be verified in the system. Or regularly check the person in charge of all customers. If there is no follow-up, opportunity or contract information, the person in charge of the customer can be deleted.
- As for the attachment, the best way is to put the customer in the attachment, but after looking at the open platform and background development documents, there is no way to obtain the attachment information in the approval. Sorry. You can only enter multiple customers directly
Get approval records of nails
Nailing requires that only 20 records can be obtained at a time. If you want to get more, you can only turn the page to obtain.
In fact, about the open platform of nails, the explanation is very detailed, according to the requirements of parameters can be passed. Nails are now Java development, and. NET has to write by itself.
For many years. NET programmer uncle, this is not a matter, easy to handle
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 "Customer name": { customers = field.value.ToString().Trim(); break; } case "Person in charge to be added": { 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 "Select any current account owner": { 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); } } } }
Scheduled tasks
5 minutes for approval.
According to the latest approval records, query the details of each approval record to obtain the relevant customer information and person in charge information.
After approval, write the result to the database. The data that has been grabbed will not be processed any more
Problem: there is a certain time difference between the interface processing of the nail and CRM system. Generally speaking, the nail system is relatively fast. Because the CRM system has not finished processing and the nail has finished processing the business, it may be repeated to judge the database. It is better to cache the processed approval.
#region Get process information of responsible person Task.Factory.StartNew(() => { DateTime dt = DateTime.Parse(DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd")); //5 Once a minute 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); } }); //Get process information 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; //If the entry has been processed, it will not be processed 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); } }); //Query customer by customer name ID And record customer information to database 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; } //Query whether the customer exists in the database var custid= DbAccess.Query($"select dataId from Customer{DateTime.Now.ToString("yyyyMMdd")} where text_1='{dic["customer"]}'"); if (!custid.Any()) { dic.AddOrUpdate("result", "Customer name does not exist",(k,v)=>v); Business.queueProcsResult.Enqueue(dic); //Customer name does not exist, save to database for processing results 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", "success", (k, v) => v); Business.queueProcsResult.Enqueue(dicTemp[success]); dicTemp.Remove(success); }), new Action<string>(fail => { dicTemp[fail].AddOrUpdate("result", "fail", (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
Welcome to comment
Learn to use, liberate the labor force, and spend more time in a better life.