纷享CRM与ERP对接的ParentClass

纷享CRM与ERP对接的ParentClass

# ! /usr/bin/env python3
# _*_ coding:utf-8 _*_

'''
Created on 2018年10月16日
@author: Andy
'''

from os import (path, remove)
from sys import (_getframe, exc_info)
from subprocess import(Popen, PIPE)
from time import (sleep, mktime)
from datetime import (datetime, timedelta)
from urllib.request import (Request, urlopen)
from json import (dumps, loads)
from importlib import (import_module)
from LogObject import (LogObject)


class DataObject(object):
    __LogObject = None
    __DBObject = None
    __CRMDict = {
        'appOpen': False,
        'appNo': 'fxiaoke',
        'appName': '纷享销客',
        'appURL': 'open.fxiaoke.com',
        'appCode': 'utf-8',
        'downloadPath': 'V:\\crm\\download\\',
        
        'urlAgent': 'Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko',
        'urlType': 'application/json',
        'urlCode': 'utf8',
        
        'appId': 'FSAID_abcdefg',
        'appSecret': 'abcdefg',
        'permanentCode': 'abcdefg',
        
        'openUserId': 'FSUID_abcdefg',
        'currentOpenUserId': 'FSUID_abcdefg',
        
        'expiresIn': None,
        'corpId': None,
        'corpAccessToken': None,
        
        'errorCode': None,
        'errorMessage': None,
        'errorDescription': None,
        
        'urlCount': 0,
        'offset': 0,
        'limit': 100,
        'apiName': None,
        'datas': None,
    }
    
    __ERRORDict = {
         '-2': '系统错误',
         '-1': '系统繁忙',
         '0': '请求成功',
         '10001': '缺少参数appId',
         '10002': '缺少参数appSecret',
         '10003': '缺少参数appAccessToken',
         '10004': '缺少参数redirectUri',
         '10005': '缺少参数responseType',
         '10006': '缺少参数scope',
         '10007': '缺少参数state',
         '10008': '缺少参数code',
         '10009': '缺少参数appAccount',
         '10010': '缺少参数openUserId',
         '10012': '缺少参数permanentCode',
         '10013': '缺少参数corpAccessToken',
         '10014': '缺少参数corpId',
         '10015': '缺少参数toUser',
         '11001': '参数appId不合法',
         '11002': '参数appSecret不合法',
         '11003': '参数appAccessToken不合法',
         '11004': '参数redirectUri不合法',
         '11005': '参数responseType不合法',
         '11006': '参数scope不合法',
         '11007': '参数state不合法',
         '11008': '参数openUserId不合法',
         '11009': '参数departmentId不合法',
         '11010': '参数code不合法',
         '11011': '参数appAccount不合法',
         '11013': '参数permanentCode不合法',
         '11014': '参数corpAccessToken不合法',
         '11015': '参数corpId不合法',
         '11016': '参数toUser不合法',
         '11017': '参数msgType不合法',
         '11018': '参数templateId不合法',
         '12002': '登录状态错误',
         '12003': '未支持的消息类型',
         '12004': 'POST的数据包为空',
         '12005': '文本消息内容为空',
         '14001': '接口调用超过限制',
         '15002': '参数不合法',
         '15003': 'APP没有访问权限',
         '20005': 'accessToken不存在或者已经过期',
         '20006': 'appId或appSecret错误',
         '20010': 'CODE不存在或者已经过期',
         '20012': 'openUserId未找到',
         '20014': '应用没有获取该员工的数据的权限',
         '20015': '永久授权码错误',
         '20016': 'corpAccessToken不存在或者已经过期',
         '20017': 'corpId未找到',
         '20020': '应用没有获取该企业的数据的权限',
         '20021': '在当前企业下,该app的状态为停用',
         '20022': '企业没有对该app授权',
         '20023': 'APP没有访问department的权限',
         '30007': '部门不存在',
         '30027': '员工不存在',
         '32000': '参数错误',
         '40010': 'templateId不合法'
    }   
    
    def __init__(self):
        object.__init__(self)

    @property
    def LogObject(self):
        return self.__LogObject

    @LogObject.setter
    def LogObject(self, logObject):
        self.__LogObject = logObject

    @property
    def DBObject(self):
        return self.__DBObject

    @DBObject.setter
    def DBObject(self, dbObject):
        self.__DBObject = dbObject

    @property
    def CRMDict(self):
        return self.__CRMDict

    @CRMDict.setter
    def CRMDict(self, crmDict):
        self.__CRMDict = crmDict

    @property
    def ERRORDict(self):
        return self.__ERRORDict
 
    def ReloadObject(self, objectNo):
        CRMModule = import_module('data.{}'.format(objectNo))
        CRMObject = getattr(CRMModule, 'DataObject')
        
        DataObject = CRMObject()
        DataObject.CRMDict = self.CRMDict
        DataObject.LogObject = self.LogObject
        DataObject.DBObject = self.DBObject
        try:
            DataObject.CreateObject()      
        finally:
            del DataObject
                      
    def PingDevice(self, ThreadID, langNo = 'GBK'):
        FunName = _getframe().f_code.co_name
        self.LogInfo = 'Thread-{}, DeviceNo={}, DeviceName={}, DeviceIP={}'.format(ThreadID, self.CRMDict['appNo'], self.CRMDict['appName'], self.CRMDict['appURL'])
        try:
            with Popen('ping -l 1 -n 1 {}'.format(self.CRMDict['appURL']), stdin = PIPE, stdout = PIPE, stderr = PIPE, shell = True) as file:
                dataStr = file.stdout.read().decode(langNo)
                if langNo == 'GBK':
                    if dataStr.find('已接收 = 1') > 0:
                        self.LogObject.warning('{} Success: {}'.format(FunName, self.LogInfo))
                        return True
        except:
            self.LogObject.error('{} Except: {}, {}'.format(FunName, self.LogInfo, exc_info()[0], exc_info()[1]))
        return False   
    
    # 获取 CorpAccessToken
    def OpenDevice(self):
        FunName = _getframe().f_code.co_name
        self.CRMDict['appOpen'] = False
        urlData = {
            'url': 'https://open.fxiaoke.com/cgi/corpAccessToken/get/V2',
            'data': {
                'appId': self.CRMDict['appId'],
                'appSecret': self.CRMDict['appSecret'],
                'permanentCode': self.CRMDict['permanentCode']
            }
        }
        if self.OpenDeviceLoadURL(urlData['url'], urlData['data']):
            # 获取 AppAccessToken
            urlData = {
                'url': 'https://open.fxiaoke.com/cgi/appAccessToken/get',
                'data': {
                    'appId': self.CRMDict['appId'],
                    'appSecret': self.CRMDict['appSecret']
                }
            }
            if self.OpenDeviceLoadURL(urlData['url'], urlData['data']): 
                sleep(1)
                self.CRMDict['appOpen'] = True
                self.LogObject.warning('{} Success: {}'.format(FunName, self.CRMDict['appOpen']))
                return True

#                 # 获取 JsapiTicket
#                 urlData = {
#                     'url': 'https://open.fxiaoke.com/cgi/jsApiTicket/get',
#                     'data': {
#                         'corpAccessToken': self.CRMDict['corpAccessToken'],
#                         'corpId': self.CRMDict['corpId'],
#                     }
#                 }
#                 if self.LoadURL(urlData['url'], urlData['data']):
#                     pass
        return False

    def CloseDevice(self):
        return False

    def OpenDeviceLoadURL(self, urlStr, urlData):
        FunName = _getframe().f_code.co_name
        if urlStr is None or urlData is None: return False
        self.CRMDict['errorCode'] = None
        self.CRMDict['errorMessage'] = None
        self.CRMDict['errorDescription'] = None
        try:
            headerStr = {
                'User-Agent': self.CRMDict['urlAgent'],
                'Content-Type': self.CRMDict['urlType']
            }
            jsonData = bytes(dumps(urlData), self.CRMDict['urlCode'])
            req = Request(url=urlStr, data=jsonData, headers=headerStr, method='POST')
            res = urlopen(req)

            if not res is None and res.reason == 'OK':
                resData = res.read().decode(self.CRMDict['appCode'])
                jsonDict = loads(resData)
                if isinstance(jsonDict, dict):
                    self.CRMDict.update(jsonDict)
                    if self.CRMDict['errorCode'] == 0 and self.CRMDict['errorMessage'] == 'success':
                        self.LogObject.info('{} Success: {}, {}'.format(FunName, urlStr, jsonDict))
                        return True
                    else:
                        self.LogObject.warning('{} Error: {}, {}'.format(FunName, urlStr, jsonDict))
                        return False
        except:
            self.LogObject.error('{} Except: {}, {}'.format(FunName, urlStr, exc_info()[0], exc_info()[1]))
        return False
    
    def PostURL(self, urlStr = None, urlData = None, urlMethod = 'POST', userAgent = 'Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko', contentType = 'application/json'):
        FunName = _getframe().f_code.co_name
        if urlStr is None or urlData is None:
            return False, None
        try:
            headerStr = {
                'User-Agent': userAgent,
                'Content-Type': contentType
            }
            jsonData = bytes(dumps(urlData),'utf8')
            req = Request(url=urlStr, data=jsonData, headers=headerStr, method=urlMethod)
            res = urlopen(req)
            self.LogObject.warning('{} Success: {}'.format(FunName, urlStr))
            return True, res
        except:
            self.LogObject.error('{} Except: {}, {}'.format(FunName, urlStr, exc_info()[0], exc_info()[1]))
        return False, None

    def LoadURL(self, urlStr, urlData):
        FunName = _getframe().f_code.co_name
        if urlStr is None or urlData is None: return False
        self.CRMDict['errorCode'] = None
        self.CRMDict['errorMessage'] = None
        self.CRMDict['errorDescription'] = None

        if self.CRMDict['urlCount'] == 800:
            self.CRMDict['urlCount'] = 0
            sleep(24) # 速度太快,会被踢
        self.CRMDict['urlCount'] = self.CRMDict['urlCount'] + 1
        
        try:
            headerStr = {
                'User-Agent': self.CRMDict['urlAgent'],
                'Content-Type': self.CRMDict['urlType']
            }
            jsonData = bytes(dumps(urlData), self.CRMDict['urlCode'])
            req = Request(url=urlStr, data=jsonData, headers=headerStr, method='POST')
            res = urlopen(req)

            if not res is None and res.reason == 'OK':
                resData = res.read().decode(self.CRMDict['appCode'])
                jsonDict = loads(resData)
                if isinstance(jsonDict, dict):
                    self.CRMDict.update(jsonDict)
                    if self.IsSuccess():
                        self.LogObject.info('{} Success: {}, {}'.format(FunName, urlStr, jsonDict))
                        return True
                    else:
                        self.LogObject.warning('{} Error: {}, {}'.format(FunName, urlStr, jsonDict))
                        if self.CRMDict['errorCode'] == 20016 and self.CRMDict['appOpen']:
                            if self.PingDevice(1):
                                if self.OpenDevice():    
                                    return self.OpenDeviceLoadURL(urlStr, urlData)
                        return False
        except:
            self.LogObject.error('{} Except: {}, {}'.format(FunName, urlStr, exc_info()[0], exc_info()[1]))
        return False

    def DownLoadURL(self, urlStr, urlData, urlFile):
        FunName = _getframe().f_code.co_name
        if urlStr is None or urlData is None: return False
        self.CRMDict['errorCode'] = None
        self.CRMDict['errorMessage'] = None
        self.CRMDict['errorDescription'] = None

        if self.CRMDict['urlCount'] == 800:
            self.CRMDict['urlCount'] = 0
            sleep(24) # 速度太快,会被踢
        self.CRMDict['urlCount'] = self.CRMDict['urlCount'] + 1
        
        try:
            headerStr = {
                'User-Agent': self.CRMDict['urlAgent'],
                'Content-Type': self.CRMDict['urlType']
            }
            jsonData = bytes(dumps(urlData), self.CRMDict['urlCode'])
            req = Request(url=urlStr, data=jsonData, headers=headerStr, method='POST')
            res = urlopen(req)
            if not res is None and res.reason == 'OK':
                if path.exists(urlFile) or path.isfile(urlFile):
                    remove(urlFile)
                    
                resData = res.read()
                with open(urlFile, 'wb') as file: 
                    file.write(resData)
                    
                if path.getsize(urlFile) == 0:
                    remove(urlFile)

                if path.exists(urlFile) and path.isfile(urlFile):    
                    self.LogObject.warning('{} Success: {}'.format(FunName, urlFile))
                    return True
            
            self.LogObject.warning('{} Error: {} {}'.format(FunName, urlFile, res.reason))
            return False
        except:
            self.LogObject.error('{} Except: {}, {}'.format(FunName, urlStr, exc_info()[0], exc_info()[1]))
        return False

    def DownLoadDataObject(self, mediaId = None, mediaFile = None):
        if mediaId is None: return False
        if not isinstance(mediaId, str): return False
        urlData = {
            'url': 'https://open.fxiaoke.com/media/download',
            'data': {
                'corpAccessToken': self.CRMDict['corpAccessToken'],
                'corpId': self.CRMDict['corpId'],
                "mediaId": mediaId,
            }
        }
        return self.DownLoadURL(urlData['url'], urlData['data'], mediaFile)

    def IsSuccess(self):
        if self.CRMDict['errorCode'] == 0 and self.CRMDict['errorMessage'] == 'success': return True
        return False
        
    def GetDictData(self, dataDict, dataKey, dataType="string"):
        if dataKey in dataDict.keys():
            if isinstance(dataDict[dataKey], list):
                if len(dataDict[dataKey]) > 0: return str(dataDict[dataKey])
            else:
                data = dataDict.get(dataKey,'')
                if not data is None:
                    if isinstance(data, bool): return 1 if data else 0
                    elif isinstance(data, int): 
                        if dataType == 'timestamp': return (datetime.utcfromtimestamp(data/1000)).strftime("%Y-%m-%d %H:%M:%S.%f")
                        return str(data)
                    return data
        return ''

    def QueryObjectDescribe(self, objectNo = None):
        if not objectNo is None: self.CRMDict['apiName'] = objectNo
        urlData = {
            'url': ' https://open.fxiaoke.com/cgi/crm/object/describe',
            'data': {
                'corpAccessToken': self.CRMDict['corpAccessToken'],
                'corpId': self.CRMDict['corpId'],
                'currentOpenUserId': self.CRMDict['currentOpenUserId'],
                'apiName': self.CRMDict['apiName'],
            }
        }
        return self.LoadURL(urlData['url'], urlData['data'])
        
    def QueryDataObject(self, objectNo = None, objectOffset = 0):
        if not objectNo is None: self.CRMDict['apiName'] = objectNo
        if not objectOffset is None: self.CRMDict['offset'] = objectOffset
        urlData = {
            'url': 'https://open.fxiaoke.com/cgi/crm/data/query',
            'data': {
                'corpAccessToken': self.CRMDict['corpAccessToken'],
                'corpId': self.CRMDict['corpId'],
                'currentOpenUserId': self.CRMDict['currentOpenUserId'],
                'apiName': self.CRMDict['apiName'],
                'searchQuery': {'offset': self.CRMDict['offset'], 'limit': self.CRMDict['limit']}
            }
        }
        return self.LoadURL(urlData['url'], urlData['data'])
    
    def QueryDataObjectRange(self, objectNo = None, objectOffset = 0, queryRange = dict):
        if not objectNo is None: self.CRMDict['apiName'] = objectNo
        if not objectOffset is None: self.CRMDict['offset'] = objectOffset
        urlData = {
            'url': 'https://open.fxiaoke.com/cgi/crm/data/query',
            'data': {
                'corpAccessToken': self.CRMDict['corpAccessToken'],
                'corpId': self.CRMDict['corpId'],
                'currentOpenUserId': self.CRMDict['currentOpenUserId'],
                'apiName': self.CRMDict['apiName'],
                'searchQuery': {'offset': self.CRMDict['offset'], 'limit': self.CRMDict['limit'], 'rangeConditions': [{'fieldName': 'last_modified_time', 'from': queryRange['from'], 'to': queryRange['to']}]}
            }
        }
        return self.LoadURL(urlData['url'], urlData['data'])
        
    def QueryDataObjectConditions(self, objectNo = None, queryRange = dict):
        if not objectNo is None: self.CRMDict['apiName'] = objectNo
        urlData = {
            'url': 'https://open.fxiaoke.com/cgi/crm/data/query',
            'data': {
                'corpAccessToken': self.CRMDict['corpAccessToken'],
                'corpId': self.CRMDict['corpId'],
                'currentOpenUserId': self.CRMDict['currentOpenUserId'],
                'apiName': self.CRMDict['apiName'],
                'searchQuery': {'offset': 0, 'limit': self.CRMDict['limit'], 'conditions': [{'conditionType': 'term_condition', 'conditions': queryRange}]}
            }
        }
        return self.LoadURL(urlData['url'], urlData['data'])
    
    def CreateDataObject(self, objectNo = None, dataDict = None):
        if dataDict is None: return False
        if not isinstance(dataDict, dict): return False
        if not objectNo is None: self.CRMDict['apiName'] = objectNo
        urlData = {
            'url': 'https://open.fxiaoke.com/cgi/crm/data/create',
            'data': {
                'corpAccessToken': self.CRMDict['corpAccessToken'],
                'corpId': self.CRMDict['corpId'],
                'currentOpenUserId': self.CRMDict['currentOpenUserId'],
                'apiName': self.CRMDict['apiName'],
                'data': dataDict
            }
        }
        return self.LoadURL(urlData['url'], urlData['data'])

    def DropDataObject(self, objectNo = None, dataID = None):
        if dataID is None: return False
        if not isinstance(dataID, str): return False
        if not objectNo is None: self.CRMDict['apiName'] = objectNo
        urlData = {
            'url': 'https://open.fxiaoke.com/cgi/crm/data/drop',
            'data': {
                'corpAccessToken': self.CRMDict['corpAccessToken'],
                'corpId': self.CRMDict['corpId'],
                'currentOpenUserId': self.CRMDict['currentOpenUserId'],
                'apiName': self.CRMDict['apiName'],
                "dataId": dataID
            }
        }
        return self.LoadURL(urlData['url'], urlData['data'])
    
    def DeleteDataObject(self, objectNo = None, dataID = None):
        if dataID is None: return False
        if not isinstance(dataID, str): return False
        if not objectNo is None: self.CRMDict['apiName'] = objectNo
        urlData = {
            'url': 'https://open.fxiaoke.com/cgi/crm/data/delete',
            'data': {
                'corpAccessToken': self.CRMDict['corpAccessToken'],
                'corpId': self.CRMDict['corpId'],
                'currentOpenUserId': self.CRMDict['currentOpenUserId'],
                'apiName': self.CRMDict['apiName'],
                "dataId": dataID
            }
        }
        return self.LoadURL(urlData['url'], urlData['data'])
        
    def UpdateDataObject(self, objectNo = None, dataID = None, dataDict = None):
        if dataID is None: return False
        if not isinstance(dataID, str): return False
        if dataDict is None: return False
        if not isinstance(dataDict, dict): return False        
        if not objectNo is None: self.CRMDict['apiName'] = objectNo
        urlData = {
            'url': 'https://open.fxiaoke.com/cgi/crm/data/update',
            'data': {
                'corpAccessToken': self.CRMDict['corpAccessToken'],
                'corpId': self.CRMDict['corpId'],
                'currentOpenUserId': self.CRMDict['currentOpenUserId'],
                'apiName': self.CRMDict['apiName'],
                "dataId": dataID,
                "data": dataDict
            }
        }
        return self.LoadURL(urlData['url'], urlData['data'])
                
    def QueryDataOption(self, tableNo = None, fieldList = list()):
        FunName = _getframe().f_code.co_name
        dataSQL = list()
        dataList = list()
        dataDict = dict()
        maxTuple = tuple()
        #0
        dataSQL.append('''
 SELECT NVL(MAX(A10.tc_dict05),0) AS datacount
 FROM tc_dict_file A10
 WHERE (1 = 1)
 AND A10.tc_dict01 = 'crm' AND A10.tc_dict02 = :v1
        ''')
        #1
        dataSQL.append('''
 UPDATE tc_dict_file A10
 SET A10.tc_dict04 = :v3
 WHERE (1 = 1)
 AND A10.tc_dict01 = 'crm' AND A10.tc_dict02 = :v1 AND tc_dict03 = :v2
        ''')        
        #2
        dataSQL.append('''
 INSERT INTO tc_dict_file( 
 tc_dict01, tc_dict02, tc_dict05, tc_dict03, tc_dict04, 
 tc_dictcuser, tc_dictcdate)
 SELECT 'crm', :1, :2, :3, :4,
 'AUTO', SYSDATE
 FROM dual
 WHERE NOT EXISTS(SELECT 1 FROM tc_dict_file C1 WHERE C1.tc_dict01 = 'crm' AND C1.tc_dict02 = :5 AND C1.tc_dict03 = :6)
        ''')
            
        try:    
            if self.QueryObjectDescribe(tableNo):
                for fieldNo in self.CRMDict['objectDesc']['fields']:
                    if self.CRMDict['objectDesc']['fields'][fieldNo]['type'] == 'select_one':
                        dataDict = {
                            'v1': '{}.{}'.format(tableNo, fieldNo),            
                        }
                        maxTuple = self.DBObject.DBQueryOne(True, dataSQL[0], dataDict)
                        if maxTuple is None or isinstance(maxTuple, tuple) or len(maxTuple) < 1 or maxTuple[0] < 1: maxTuple = (0,)

                        for data in self.CRMDict['objectDesc']['fields'][fieldNo]['options']:
                            dataDict = {    
                                'v1': '{}.{}'.format(tableNo, fieldNo),
                                'v2': data['value'],        
                                'v3': data['label'],                
                            }
                            if not self.DBObject.DBExecute(True, dataSQL[1], dataDict):
                                self.LogObject.error("{} Error: {}, {}, {}".format(FunName, '{}.{}'.format(tableNo, fieldNo), data['value'], data['label']))        

                            dataTupe = (
                                '{}.{}'.format(tableNo, fieldNo),
                                maxTuple[0]+len(dataList),
                                 data['value'],
                                 data['label'],
                                 '{}.{}'.format(tableNo, fieldNo),    
                                 data['value'],             
                             )
                            dataList.append(dataTupe)
                        if len(dataList) > 0:    
                            if self.DBObject.DBExecuteMany(True, dataSQL[2], dataList):
                                if self.DBObject.DBDataCount > 0: self.LogObject.warning('{} Success, 【{}】 CreateCount={}'.format(FunName, '{}.{}'.format(tableNo, fieldNo), self.DBObject.DBDataCount))    
        except:
            self.LogObject.error('{} Except: {}, {}'.format(FunName, exc_info()[0], exc_info()[1]))

    def GetTimestamp(self, today = None, sec = 0):
        if today is None:
            return int(mktime((datetime.now()+timedelta(seconds=sec)).timetuple())*1000)
        d = datetime.strptime(today,'%Y-%m-%d %H:%M:%S.%f')+timedelta(seconds=sec)
        return int((mktime(d.timetuple())+d.microsecond/1000000)*1000)  

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值