EC_招商CRM.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. # -*- codeing = utf-8 -*-
  2. # @Time : 2022/8/29 14:43
  3. # @Author : Clown
  4. # @File : EC_招商CRM.py
  5. # @Software : PyCharm
  6. import requests
  7. import hashlib
  8. import time
  9. import csv
  10. import json
  11. import pandas as pd
  12. import math
  13. from datetime import timedelta, datetime
  14. from dateutil.parser import parse
  15. # corpid : 17409173
  16. # get_sign : 签名算法
  17. def get_sign(app_id, app_secret, timestamp):
  18. '''
  19. :param app_id: 应用ip 914890189023739904
  20. :param app_secret: 应用密钥 Ur2K41t71RrxYn7eWhN
  21. :param timestamp: 时间戳 毫秒
  22. :return: 加密密文
  23. '''
  24. str = 'appId=' + app_id + '&appSecret=' + app_secret + '&timeStamp=' + timestamp
  25. hl = hashlib.md5()
  26. hl.update(str.encode(encoding='utf-8'))
  27. return hl.hexdigest().upper()
  28. def getCustomDet(corpid,sign,timestamp,pageNo):
  29. url = 'https://open.workec.com/v2/customer/queryList'
  30. headers = {'Content-Type':'application/json',
  31. 'X-Ec-Cid':corpid,
  32. 'X-Ec-Sign':sign,
  33. 'X-Ec-TimeStamp':timestamp}
  34. # 全局浏览
  35. # json = {
  36. # "__grouble_id": "x"
  37. # }
  38. # 排序浏览 asc升序,desc降序
  39. json = {
  40. "pageNo": pageNo,
  41. "pageSize": 50,
  42. 'orderBy':[{"sortField":'createTime',
  43. "sortType":'desc'}]
  44. }
  45. resp = requests.post(url,headers=headers,json=json).json()
  46. # print(resp)
  47. return resp
  48. def selectCustomInfo(corpid,sign,timestamp,selectInfoFile,df_out_path):
  49. df = pd.read_excel(selectInfoFile,dtype=str)
  50. infoList = df['号码']
  51. print(len(infoList))
  52. df_out = []
  53. infoList50 = []
  54. for n in range(round(len(infoList)/50+1)):
  55. x = 50 * n
  56. y = 50 * (n + 1)
  57. info = ','.join(infoList[x:y])
  58. url = 'https://open.workec.com/v2/customer/queryExist'
  59. headers = {'Content-Type': 'application/json',
  60. 'X-Ec-Cid': corpid,
  61. 'X-Ec-Sign': sign,
  62. 'X-Ec-TimeStamp': timestamp}
  63. json_params = {"mobile":str(info)}
  64. resps = requests.post(url,headers=headers,json=json_params).json()['data']
  65. for resp in resps:
  66. df_out_dict = {}
  67. num = resp['num']
  68. df_out_dict['mobile'] = resp['mobile']
  69. df_out_dict['queryExistNum'] = num
  70. print(info,num)
  71. df_out.append(df_out_dict)
  72. n += 1
  73. df_out = pd.DataFrame(df_out)
  74. df_out.to_excel(df_out_path)
  75. def getOkCustom(corpid,sign,timestamp,pageNo):
  76. url = 'https://open.workec.com/v2/customer/queryList'
  77. headers = {'Content-Type': 'application/json',
  78. 'X-Ec-Cid': corpid,
  79. 'X-Ec-Sign': sign,
  80. 'X-Ec-TimeStamp': timestamp}
  81. # 全局浏览
  82. # json = {
  83. # "__grouble_id": "x"
  84. # }
  85. # 排序浏览 asc升序,desc降序
  86. json = {
  87. "labelIds":[83625192],
  88. "pageNo": pageNo,
  89. "pageSize": 50,
  90. 'orderBy': [{"sortField": 'createTime',
  91. "sortType": 'desc'}]
  92. }
  93. resp = requests.post (url, headers=headers, json=json).json()
  94. # print(resp)
  95. return resp
  96. def getInfo(users_dict,corpid,sign,timestamp,pagesNo,out_file):
  97. f = open(out_file,mode='w',encoding="utf-8-sig",newline='')
  98. csv_writer = csv.writer(f)
  99. title = ['创建日期','资源id','资源来源','最后跟进人','组别','最后跟进时间','备注']
  100. csv_writer.writerow(title)
  101. for pageNo in range(pagesNo):
  102. info_data = getCustomDet(corpid,sign,timestamp,pageNo+1)
  103. for info in info_data['data']['list']:
  104. createTime = info['createTime']
  105. crmId = info['crmId']
  106. channel = info['channel']
  107. lastFollowUserId = 'userID:' +str(info['lastFollowUserId'])
  108. try:
  109. lastFollowUser = users_dict[lastFollowUserId]['userName']
  110. deptName = users_dict[lastFollowUserId]['deptName']
  111. except:
  112. lastFollowUser = lastFollowUserId
  113. deptName = '未知'
  114. try:
  115. lastContactTime = info['lastContactTime']['time']
  116. except:
  117. lastContactTime = ''
  118. try:
  119. memo = info['memo']
  120. except:
  121. memo = ''
  122. row = [createTime,crmId,channel,lastFollowUser,deptName,lastContactTime,memo]
  123. csv_writer.writerow(row)
  124. print('page:'+ str(pageNo) + 'ok')
  125. def getOkInfo(users_dict,corpid,sign,timestamp,pagesNo,out_file):
  126. f = open(out_file,mode='w',encoding="utf-8-sig",newline='')
  127. csv_writer = csv.writer(f)
  128. title = ['创建日期','资源id','资源来源','最后跟进人','组别','最后跟进时间','备注']
  129. csv_writer.writerow(title)
  130. for pageNo in range(pagesNo):
  131. info_data = getOkCustom(corpid,sign,timestamp,pageNo+1)
  132. for info in info_data['data']['list']:
  133. createTime = info['createTime']
  134. crmId = info['crmId']
  135. channel = info['channel']
  136. lastFollowUserId = 'userID:' +str(info['lastFollowUserId'])
  137. try:
  138. lastFollowUser = users_dict[lastFollowUserId]['userName']
  139. deptName = users_dict[lastFollowUserId]['deptName']
  140. except:
  141. lastFollowUser = lastFollowUserId
  142. deptName = '未知'
  143. try:
  144. lastContactTime = info['lastContactTime']['time']
  145. except:
  146. lastContactTime = ''
  147. try:
  148. memo = info['memo']
  149. except:
  150. memo = ''
  151. row = [createTime,crmId,channel,lastFollowUser,deptName,lastContactTime,memo]
  152. csv_writer.writerow(row)
  153. print('page:'+ str(pageNo) + 'ok')
  154. def getUserInfo(corpid,sign,timestamp):
  155. #获取组织架构信息,主要是对应上id和人名
  156. url = 'https://open.workec.com/v2/org/struct/info'
  157. params = 'needUser=true'
  158. headers = {'Content-Type': 'application/json',
  159. 'X-Ec-Cid': corpid,
  160. 'X-Ec-Sign': sign,
  161. 'X-Ec-TimeStamp': timestamp}
  162. resp = requests.get(url,params=params,headers=headers).json()
  163. return resp
  164. # def runT():
  165. # pages = OrdersInfoByPages()
  166. # executor = ThreadPoolExecutor (pages)
  167. # for i in range(pages):
  168. # offset = i*100
  169. # executor.submit (lookupPagesInfo, offset)
  170. # executor.shutdown(wait=True)
  171. def getLabel(corpid,sign,timestamp):
  172. url = 'https://open.workec.com/v2/label/getLabelInfo'
  173. json_params = {'groupValue':''}
  174. headers = {'Content-Type': 'application/json',
  175. 'X-Ec-Cid': corpid,
  176. 'X-Ec-Sign': sign,
  177. 'X-Ec-TimeStamp': timestamp}
  178. resp = requests.post(url, json=json_params, headers=headers).text
  179. print(resp)
  180. def addCustomers(corpid,sign,timestamp,customerList):
  181. #新增客户
  182. #来源渠道备注:47625为400电话,
  183. #customerList = [{}]
  184. url = 'https://open.workec.com/v2/customer/addCustomer'
  185. headers = {'Content-Type': 'application/json',
  186. 'X-Ec-Cid': corpid,
  187. 'X-Ec-Sign': sign,
  188. 'X-Ec-TimeStamp': timestamp}
  189. json_params = {'optUserId':17409174,
  190. 'list':customerList,
  191. 'notify':False,
  192. 'repeat':False,
  193. 'supply_detail':False}
  194. resp = requests.post(url,headers=headers,json=json_params).json()
  195. failureList = resp['data']['failureList']
  196. successIdList = resp['data']['successIdList']
  197. if len(successIdList) == 1:
  198. print('ok')
  199. elif len(failureList) == 1:
  200. print('error')
  201. def getChannelSource(corpid,sign,timestamp):
  202. #查询来源信息
  203. url= 'https://open.workec.com/v2/customer/getChannelSource'
  204. headers = {'Content-Type': 'application/json',
  205. 'X-Ec-Cid': corpid,
  206. 'X-Ec-Sign': sign,
  207. 'X-Ec-TimeStamp': timestamp}
  208. resp = requests.get(url,headers=headers).json()
  209. print(json.dumps(resp,ensure_ascii=False))
  210. def findUserInfoById(corpid,sign,timestamp,userId):
  211. url = 'https://open.workec.com/v2/org/user/findUserInfoById'
  212. headers = {'Content-Type': 'application/json',
  213. 'X-Ec-Cid': corpid,
  214. 'X-Ec-Sign': sign,
  215. 'X-Ec-TimeStamp': timestamp}
  216. json_params = {'userId':str(userId),'deptInfo':False}
  217. resp = requests.post(url,headers=headers,json=json_params).json()
  218. # print(resp)
  219. return resp
  220. def getCustomersTrajectory(corpid,sign,timestamp,trajectoryRequestDict):
  221. state_dict = {"1001": "发送短信", "1002": "拨打对方电话", "1003": "网站客服会话", "1004": "EC会话", "1005": "QQ会话", "1006": "发送邮件",
  222. "1007": "接听对方电话", "1008": "接收邮件", "1009": "电话会议", "1010": "拨打云总机电话", "1011": "接听云总机电话",
  223. "2003": "日历提醒", "2001": "定时提醒", "2002": "提醒销售计划", "3001": "新增客户", "3002": "新增客户资料", "3003": "更新客户资料",
  224. "3004": "更新客户标签", "3005": "关联EC", "3006": "取消EC关联", "3007": "关联QQ", "3008": "取消QQ关联", "3009": "转让客户",
  225. "3010": "合并客户", "3011": "分配客户", "3012": "领取客户", "3013": "放弃客户", "3014": "添加销售计划", "3015": "修改销售计划",
  226. "3016": "使用销售模板", "3017": "更新客户阶段", "3018": "关联公司", "3019": "取消公司关联", "3020": "上传头像",
  227. "3021": "新增共享同事", "3022": "取消共享同事", "3023": "退出共享关系", "3024": "转为公司导入", "4000": "添加跟进记录",
  228. "6000": "微信活动", "7000": "拜访客户"}
  229. crmIds = trajectoryRequestDict['crmIds']
  230. lastContactTime = trajectoryRequestDict['lastContactTime']
  231. memo = trajectoryRequestDict['memo'] #memo决定是否为首次获取日志
  232. #查询用户信息
  233. url_crmInfo = 'https://open.workec.com/v2/customer/queryList'
  234. json_params_crmInfo = {'crmIds': [crmIds]}
  235. url = 'https://open.workec.com/v2/customer/getTrajectory'
  236. headers = {'Content-Type': 'application/json',
  237. 'X-Ec-Cid': corpid,
  238. 'X-Ec-Sign': sign,
  239. 'X-Ec-TimeStamp': timestamp}
  240. resp_crmInfo = requests.post (url_crmInfo, headers=headers, json=json_params_crmInfo).json ()['data']
  241. # print(resp_crmInfo)
  242. #获取当前跟进人
  243. try:
  244. followerName = findUserInfoById (corpid, sign, timestamp, resp_crmInfo['list'][0]['lastFollowUserId'])['data']['name']
  245. except:
  246. followerName = '离职员工'
  247. #获取创建时间及最近一次跟进时间
  248. try:
  249. createTime = resp_crmInfo['list'][0]['createTime']
  250. lastTime = resp_crmInfo['list'][0]['contactTime']
  251. except:
  252. createTime = resp_crmInfo['list'][0]['createTime']
  253. lastTime = createTime
  254. # print(createTime,lastTime)
  255. #依据是否首次获取跟进日志,构建日志查询参数
  256. endTime = parse (str (parse (str (lastTime)) + timedelta (seconds=2))).strftime ('%Y-%m-%d %H:%M:%S')
  257. #首次
  258. if int(memo) == 0:
  259. startTime = parse (str (parse (str (createTime)) + timedelta (seconds=-2))).strftime ('%Y-%m-%d %H:%M:%S')
  260. # 判断日期间隔
  261. n = math.ceil ((parse (str (endTime)).timestamp () - parse (str (startTime)).timestamp ()) / 3600 / 24 / 30)
  262. if n <= 1:
  263. json_params = [{'crmIds':str(crmIds),
  264. 'date':{'startTime':startTime,
  265. 'endTime':endTime}}]
  266. #日期间隔大于30天
  267. else:
  268. json_params = []
  269. x = 1
  270. for i in range (n):
  271. if x < n:
  272. m = parse (str (parse (str (startTime)) + timedelta (days=30))).strftime ('%Y-%m-%d %H:%M:%S')
  273. x += 1
  274. else:
  275. m = endTime
  276. json_param = {'crmIds':str(crmIds),
  277. 'date':{'startTime':startTime,
  278. 'endTime':m}}
  279. json_params.append(json_param)
  280. startTime = parse (str (parse (str (m)) + timedelta (seconds=1))).strftime ('%Y-%m-%d %H:%M:%S')
  281. # 二次
  282. elif int(memo) == 1:
  283. startTime = parse (str (parse (str (lastContactTime.replace('T',' '))) + timedelta (seconds=1))).strftime ('%Y-%m-%d %H:%M:%S')
  284. json_params = [{'crmIds': str (crmIds),
  285. 'date': {'startTime': startTime,
  286. 'endTime': endTime}}]
  287. log = ''
  288. for json_param in json_params:
  289. resp = requests.post(url,headers=headers,json=json_param).json()['data']
  290. trajectoryList = resp['trajectoryList']
  291. #当前时间内是否有更新
  292. if len(trajectoryList) == 0:
  293. trajectoryResult = {'trajectoryLog': log,
  294. 'result':-2,
  295. 'followerName': followerName,
  296. 'lastContactTime': lastTime.replace (' ', 'T')}
  297. else:
  298. for trajectory in trajectoryList:
  299. createTime = trajectory['createTime']
  300. trajectoryType = trajectory['trajectoryType']
  301. trajectoryName = state_dict[str(trajectoryType)]
  302. userId = trajectory['userId']
  303. try:
  304. userName = findUserInfoById (corpid, sign, timestamp, userId)['data']['name']
  305. if userName == '谢总':
  306. userName = '系统分配'
  307. else:
  308. ...
  309. except:
  310. userName = '员工已离职'
  311. if trajectoryType == 4000:
  312. recordInfo = f"\n备注:【{trajectory['content']}】"
  313. elif trajectoryType in [3009]:
  314. receiveUserIds = trajectory['receiveUserIds']
  315. try:
  316. receiveUserName = findUserInfoById (corpid, sign, timestamp, receiveUserIds)['data']['name']
  317. except:
  318. receiveUserName = '离职员工'
  319. recordInfo = f'\n备注:客户转让至【{receiveUserName}】'
  320. else:
  321. recordInfo = ''
  322. text_out = f'{createTime}--跟进动作:{trajectoryName}--操作人:{userName}{recordInfo}'
  323. # print(text_out)
  324. log = log + text_out + '\n'
  325. # print(trajectoryType)
  326. if trajectoryType == 3013:
  327. mark = -1
  328. elif trajectoryType in [3001,3011,3012,3009]:
  329. mark = 0
  330. else:
  331. mark = 1
  332. trajectoryResult = {'trajectoryLog':log,
  333. 'result':mark,
  334. 'followerName':followerName,
  335. 'lastContactTime':lastTime.replace(' ','T')}
  336. # print(trajectoryResult)
  337. return trajectoryResult
  338. def runTrajectory(trajectoryRequestDict):
  339. '''
  340. result 为int 数值为0,1,-1 【0表示未跟进,1表示跟进中,-1表示客户被放弃】
  341. '''
  342. corpid = '17409173'
  343. timestamp = str (int (round (time.time () * 1000)))
  344. app_id = '914890189023739904'
  345. app_secret = 'Ur2K41t71RrxYn7eWhN'
  346. sign = get_sign (app_id, app_secret, str (timestamp))
  347. try:
  348. trajectoryResult = getCustomersTrajectory(corpid,sign,timestamp,trajectoryRequestDict)
  349. except Exception as e:
  350. print(e)
  351. print ('input Info Error1!')
  352. trajectoryResult= {'trajectoryLog': '参数有误', 'followerName': '', 'result': 'error', 'lastContactTime':'Null'}
  353. return trajectoryResult
  354. # 批量查询客户信息
  355. def selectCustomsInfo(clue_photo_num_list,cycle_hours):
  356. # 入参格式 clue_photo_num_list = ['13775234802,19232722757,19903546661,18164685587,18655626682,13152011389,18709596396,15536158166,13752862728,17767157888,13812552265,18751168688,13868980059,15994211909,15330337158,18069510127,16685833888,13989666719,18082610680,13629286297,15151093376,15349370007,18696933445,13777447476,19218748440,13765636503,18655626682,15703592119,15703592119,15996391796,16613882968,18968498788,18261563639,18012565368,18012565368,13867934378,13813841224,18969513827,13765636503,18984319715,18963978060,15514586363,13813000590,13894722167,13100716617,13100716617,15202388770,18716736100,13801584013']
  357. corpid = '17409173'
  358. timestamp = str(int(round(time.time() * 1000)))
  359. app_id = '914890189023739904'
  360. app_secret = 'Ur2K41t71RrxYn7eWhN'
  361. sign = get_sign(app_id, app_secret, str(timestamp))
  362. url = 'https://open.workec.com/v2/customer/queryList'
  363. headers = {'Content-Type': 'application/json',
  364. 'X-Ec-Cid': corpid,
  365. 'X-Ec-Sign': sign,
  366. 'X-Ec-TimeStamp': timestamp}
  367. out_list = []
  368. for group in clue_photo_num_list:
  369. params = {'mobile':group}
  370. resp = requests.post(url,headers=headers,json=params).text
  371. resp = json.loads(resp)
  372. for i in resp['data']['list']:
  373. createTime = i['createTime']
  374. noteTime = (parse(createTime)+timedelta(hours = cycle_hours)).strftime('%Y-%m-%d %H:%M:%S')
  375. crmId = i['crmId']
  376. mobile = i['mobile'][-11:]
  377. print(mobile,crmId,createTime,cycle_hours,noteTime,'')
  378. out_list.append((mobile,crmId,createTime,cycle_hours,noteTime,None,'同步crmId'))
  379. print(out_list)
  380. return out_list
  381. if __name__ == '__main__':
  382. if 1==0:
  383. corpid = '17409173'
  384. timestamp = str(int(round(time.time() * 1000)))
  385. app_id = '914890189023739904'
  386. app_secret = 'Ur2K41t71RrxYn7eWhN'
  387. sign = get_sign(app_id, app_secret, str(timestamp))
  388. # getLabel (corpid, sign, str(timestamp))
  389. crmIds = [6649346389,6716036532,6713477481]
  390. trajectoryRequestDict = {'crmIds':8428482275,'memo':0,'lastContactTime':0}
  391. getCustomersTrajectory (corpid, sign, timestamp, trajectoryRequestDict)
  392. clue_photo_num_list = ['16685833888,13765636503,18751168688,15994211909,18969513827,15330337158,13100716617,18716736100,13801584013,15900818382,18696933445,18752808628,15202388770,18069510127,18164685587,18957915191,15180810078,15161112338,18963978060,16613882968,18082610680,13752862728,13812552265,13011848788,13629286297,15349370007,13914701944,13867934378,18611013399,13777126490,13349994865,15996391796,18984319715,18968498788,18012565368,15050580681,15605853405,18131725305,15703592119,18709596396,15514586363,13851034333,13777447476,13813000590,18280502701,19154961250,13152011389,15536158166,18609680601,18655626682', '19903546661,13894722167,13989666719,13813841224,13775234802,15507281555,19218748440,15151093376,17767157888,19232722757,18762280927,18261563639,13868980059']
  393. cycle_hours = 15
  394. selectCustomsInfo(clue_photo_num_list,cycle_hours)
  395. if 1 == 0:
  396. user_data = getUserInfo (corpid, sign, str(timestamp))
  397. # print(json.dumps(user_data,ensure_ascii=False))
  398. users_dict = {}
  399. for user in user_data['data']['users']:
  400. deptId = user['deptId']
  401. if deptId == 17440441:
  402. users_dict['userID:' + str (user['userId'])] = {"userName": user['userName'], "deptName": '招商一部'}
  403. elif deptId == 17440442:
  404. users_dict['userID:' + str (user['userId'])] = {"userName": user['userName'], "deptName": '招商二部'}
  405. elif deptId == 18659257:
  406. users_dict['userID:' + str (user['userId'])] = {"userName": user['userName'], "deptName": '招商三部'}
  407. else:
  408. ...
  409. # print(json.dumps(users_dict,ensure_ascii=False))
  410. if 1 == 0:
  411. # print(sign)
  412. out_file = f'C:/Users/ClownHe/Desktop/导出/{timestamp}招商数据.csv'
  413. total_data = getCustomDet(corpid,sign,str(timestamp),1)
  414. pagesNo = total_data['data']['page']['maxPageNo']
  415. getInfo (users_dict, corpid, sign, str(timestamp), pagesNo, out_file)
  416. if 1 == 0:
  417. out_file = f'C:/Users/ClownHe/Desktop/导出/{timestamp}招商签约数据.csv'
  418. total_data = getOkCustom (corpid, sign, str (timestamp), 1)
  419. pagesNo = total_data['data']['page']['maxPageNo']
  420. getOkInfo (users_dict, corpid, sign, str (timestamp), pagesNo, out_file)
  421. getChannelSource (corpid, sign, timestamp)
  422. selectInfoFile = 'C:/Users/ClownHe/Desktop/goods/浆小白线索数据.xlsx'
  423. df_out_path = 'C:/Users/ClownHe/Desktop/goods/浆小白线索数据(核对).xlsx'
  424. selectCustomInfo (corpid, sign, str (timestamp), selectInfoFile, df_out_path)
  425. # list_a = [{
  426. # "followUserId":17409174,
  427. # "mobile":"18795412345",
  428. # "name":"测试数据请忽略",
  429. # 'memo':'400热线'
  430. # }]
  431. # addCustomers (corpid, sign, timestamp, list_a)