#!/usr/bin/env python
#coding=utf8

import httplib, urllib
import hashlib
import json
import time
import threading
import sys
import pprint

class FundApi:
    SYS_PARAMS = {'channel':3, 'format':'json', 'function':'C001', 'merid':'TKZC', 'signmode':'md5', 'version':'V1.0'}
    SYS_PASSWORD = '20140819'
    HEADERS = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
    URL_PREFIX = '/fundapi/restful/'
    FUND_CODE = '001477'

    TEST_PASSWORD = '112233'
   
    PORT = 8001
    TIMEOUT = 30
    
    #TRADE_ACCOUNT = '00152888' #147.105
    #TRADE_ACCOUNT = '00152708' #147.108
    TRADE_ACCOUNT = '00000018'  #147.104
    #TRADE_ACCOUNT = '00000018' #147.110
    #TRADE_ACCOUNT = '00011238'  #147.102

    #IP = '10.88.147.105'    #cashpro
    #IP = '10.88.147.108'    #cashpro
    IP = '10.88.147.104'    #prod
    #IP = '10.88.147.110'    #prod 高版本
    #IP = '10.88.147.102'     #cashpro银行接口测试

    #TEST_ID = '140827194801030692' #147.105
    #TEST_ID = '36012119900201399X' #147.108
    TEST_ID = '430404196903295394' #147.104
    #TEST_ID = '430404196903295394' #147.110
    #TEST_ID = '340321198608150019' #147.102

    session = ''

    def sendRequest(self, url, params):
        params = dict(params.items() + self.SYS_PARAMS.items())
        self.addSig(params)
        conn = httplib.HTTPConnection(self.IP, self.PORT, timeout = self.TIMEOUT)
        conn.request('POST', self.URL_PREFIX + url, urllib.urlencode(params), self.HEADERS);
        ret = conn.getresponse().read()
        conn.close()
        return json.loads(ret)
    
    def addSig(self, params):
        result = ''
        for k in sorted(params):
            result += (k + str(params[k]))
        result = self.SYS_PASSWORD + result + self.SYS_PASSWORD
        md5 = hashlib.new("md5", result).hexdigest()
        params['signmsg'] = md5
        return md5
    
    def fundlist(self):
        params = {
            'function': 'S010'
        }
        return self.sendRequest('query/fundstatequery', params)

    def valubankquery(self):
        params = {
            'function': 'I016',
            'sessionkey': self.session
        }
        return self.sendRequest('valuavgr/valubankquery', params)
   
    def queryrisk(self):
        params = {'function': 'C004'}
        return self.sendRequest('account/queryrisk', params)

    def ccbsignrecordquery(self):
        params = {
            'function': 'B044',
            'customno': '18991919',
            'capitalmode': 'B'
        }
        return self.sendRequest('capital/ccbsignrecordquery', params)

    def genopendata_allinpay(self):
        params = { 
            'identitytype': '0',
            'identityno': '231201196204212995',
            'customerappellation': '张兴宸',
            'capitalmode': 'M',
            'bankacco': '6217000010052562454',
            'function': 'C002',
            'bankserial': '005',
            'busitype': 'OPENACCO',
            'handset': '17600857174'
            #'email': '1@1.cn',
        }
        return self.sendRequest('account/gendata', params)

    def genopendata_chinapay(self):
        params = { 
            'identitytype': '0',
            'identityno': '321322197008124027',
            #'identityno': '140930196411245674',
            'customerappellation': '李四',
            'capitalmode': '3',
            'bankacco': '6228480987654321789',
            'function': 'C002',
            'bankserial': '003',
            'busitype': 'OPENACCO',
            'openaccochannel': '1',
            'email': '1@1.cn',
        }
        return self.sendRequest('account/gendata', params)

    def genopendata_ccb(self):
        params = { 
            'identitytype': '0',
            'identityno': '210521198908180035',
            'customerappellation': '张兴宸',
            'capitalmode': 'B',
            'bankacco': '6217000010052562454',
            'function': 'C002',
            'bankserial': '005',
            #'busitype': 'OPENACCO',
            'busitype': 'RESETPASSWORD',
            #'email': '1@1.cn',
        }
        return self.sendRequest('account/gendata', params)

    def genopendata_icbc(self):
        params = { 
            'identitytype': '0',
            'identityno': '231201196204212995',
            'customerappellation': '张兴宸',
            'capitalmode': '4',
            'bankacco': '6212260200072115211',
            'function': 'C002',
            'bankserial': '002',
            'busitype': 'OPENACCO',
            'handset': '17600857174'
            #'email': '1@1.cn',
        }
        return self.sendRequest('account/gendata', params)

    def genopendata_cmb_mobile(self):
        params = { 
            'identitytype': '0',
            'identityno': '452728194205044639',
            'customerappellation': '杨汇付',
            'capitalmode': 'G',
            'bankacco': '6228226043313330',
            'function': 'C002',
            'bankserial': '007',
            'busitype': 'OPENACCO',
            'handset': '18600237626', #汇付渠道必填
            'openaccochannel': '1', #招行手机开户
            #'email': '1@1.cn',
        }
        return self.sendRequest('account/gendata', params)

    def bankinfo(self):
        params = {'function':'C001'}
        return self.sendRequest('account/bankinfo', params)
    
    def login(self):
        params = {
            'certificatetype':'0',
            'lognumber':self.TEST_ID,
            'logtype':'2',
            'password':self.TEST_PASSWORD,
            'function':'P003'
        }
        ret = self.sendRequest('integrate/login', params)
        self.session = ret['results']['sessionkey']
        return ret
    
    def loginNoPasswd(self):
        params = {
            'certificatetype':'0',
            'lognumber':self.TEST_ID,
            'logtype':'2',
            'function':'P003'
        }
        ret = self.sendRequest('integrate/login', params)
        self.session = ret['results']['sessionkey']
        return ret

    #几十条交易记录:100qps
    #几万条交易记录:60qps
    #几条交易记录:180qps
    def sharequery(self):
        params = {
            'sessionkey':self.session,
            'fundcode':self.FUND_CODE,
            'filterzerofundshare':'0',
            'querytype':'1',
            'function':'S001'
        }
        return self.sendRequest('query/sharequery', params)

    def querytradeacco(self):
        params = {
            'sessionkey':self.session,
            'function':'C020'
        }
        return self.sendRequest('account/querytradeacco', params)

    def chinapaymobileopenaccoorder(self):
        params = {
            'mobile': '13812345678',
            'bankacco': '6228480987654321789',
            'email': '1@1.cn',
            'bankserial': '003',
            'sex': '1',
            'bankname': '农业银行',
            'identityno':'321322197008124027',
            'busintype': 'OPENACCO',
            'identitytype': '0',
            'customerappellation':'李四',
            'function': 'B026'
        }
        return self.sendRequest('capital/chinapaymobileopenaccoorder', params)

    def meropenacco(self):
        params = {
            'communicationaddr':'北京',
            'identityno':'371081194805259863',
            'reckoningmailtype':'1',
            'reckoningsendtype':'1',
            'email':'yxs@sina.com',
            'zipcode':'100010',
            'investlive':'1',
            'handset':'13466706799',
            'workcode':'1',
            'sex':'1',
            'transactorcertitype':'0',
            'transactorname':'商户开户测试',
            'tradepassword':self.TEST_PASSWORD,
            'transactorcertinumber':'13466706799',
            'identitytype':'0',
            'bankserial':'003',
            'invalidate':'20201010',
            'bankacco':'6230208899998887',
            'customerappellation':'商户开户测试',
            'capitalmode':'1',
            'bankname':'建设银行',
            'nationality2':'392',
            'longbillway':'11110000',
            'fundmanagerid':'001001',
            #'yinliancdcard':'201710231430233603',
            'riskability':'1',
            'interfacetype':'0',
            'function':'C037'
        }
        return self.sendRequest('account/meropenacco', params)

    def hisprofitquery(self):
        params = {
            'sessionkey':self.session,
            'tradeacco':self.TRADE_ACCOUNT,
            'datasource':'DS',
            'fundcode':self.FUND_CODE,
            'function':'S049'
        }
        return self.sendRequest('query/hisprofitquery', params)

    def purchaseNoPasswd(self):
        params = {
            'sessionkey':self.session,
            'tradeacco':self.TRADE_ACCOUNT,
            'fundcode':self.FUND_CODE,
            'sharetype':'A',
            'transfermoney':'1',
            'applysum':'1.23', 
            'businflag':'022',
            'detailcapitalmode':'01',
            'tradesource' : '000000060023',
            'function':'T003',
        }
        return self.sendRequest('tradereq/purchase', params)
   
    def targetfunds(self):
        params = {
            'sessionkey': self.session,
            'fundcode': '001500',
            'businflag': '036',
            'sharetype': 'A',
            'tradeacco': self.TRADE_ACCOUNT
        }
        return self.sendRequest('tradereq/targetfunds', params)

    def purchase(self):
        params = {
            'sessionkey':self.session,
            'tradeacco':self.TRADE_ACCOUNT,
            'fundcode':self.FUND_CODE,
            'sharetype':'A',
            'transfermoney':'1',
            'applysum':'1.23', 
            'businflag':'022',
            'detailcapitalmode':'01',
            'function':'T003',
            #'precheckflag': '1'
        }
        return self.sendRequest('tradereq/purchase', params)

    def confirmorder(self):
        params = {
            'applyserial':'20150910000178',
            'applysum':'12.45',
            'bankacco':'6230204129620742',
            'bankserial':'003',
            'capitalmode':'M',
            'tradeacco':'00000058',
            'confirmflag':'1',
            'notifytype':'0',
            'comefrom':'000000060023',
            'cpflag':'1',
            'function':'T030',
            'banknetdate':'20150910',
            'banknettime':'101010'
        }
        return self.sendRequest('tradereq/merconfirmorder', params);
    
    def gift(self):
        params = {
            'sessionkey':self.session,
            'tradeacco':self.TRADE_ACCOUNT,
            'fundcode':self.FUND_CODE,
            'sharetype':'A',
            'tradesource':'000000060024',
            'applysum':'9.1', 
            'businflag':'022',
            'transfermoney':'0',
            'detailcapitalmode':'04',
            'function':'T003',
        }
        pp = MyPrettyPrinter()
        pp.pprint(params);
        return self.sendRequest('tradereq/purchase', params)

    def sale(self):
        params = {
            'sessionkey':self.session,
            'applysum':'1',
            'tradeacco':self.TRADE_ACCOUNT,
            'fundcode':self.FUND_CODE,
            'saleway':'3', #3 T+0, #0 普通
            'realtimeflag':'1',
            'sharetype':'A',
            'transfermoney':'0',
            'function':'T006',
        }
        return self.sendRequest('tradereq/sale', params)

    def valutrade(self):
        params = {
            'function' :  'I006',
            'sessionkey':self.session,
            'cycleunit' :  '0', 
            'jyzq' :  '1',
            'fundcode' :  self.FUND_CODE,
            'jyrq' :  '15',
            'scjyrq' :  '000000',
            'applysum' :  '123',
            'zzrq' :  '99991231',
            'sharetype' :  'A',
            'tradeacco' :  self.TRADE_ACCOUNT,
            'tradepassword' :  self.TEST_PASSWORD
        }
        return self.sendRequest('valuavgr/trade', params)

    def valutradechange(self):
        params = {
            'sessionkey':self.session,
            'function' : 'I007',
            'cycleunit' : '0',
            'jyzq' : '1',
            'jyrq' : '15',
            'applysum' : '123',
            'zzrq' : '99991231',
            'xyh' : '201508260061',
            'tradeacco' : self.TRADE_ACCOUNT,
            'state' : 'H',
            'tradepassword' : self.TEST_PASSWORD
        }   
        return self.sendRequest('valuavgr/tradechange', params)
    
    def bonusquery(self):
        params = {
            'sessionkey':self.session,
            'applyrecordno':'100',
            'function':'S005',
        }
        return self.sendRequest('query/bonusquery', params)

    def tradeappquery(self):
        params = {
            'sessionkey':self.session,
            'applyrecordno':'100',
            'function':'S003',
        }
        return self.sendRequest('query/tradeappquery', params)
        
    def withdrawlist(self):
        params = {
            'sessionkey':self.session,
            'function':'T008',
        } 
        return self.sendRequest('tradereq/withdrawlist', params)
    
    def withdraw(self):
        params = {
            'sessionkey':self.session,
            'function':'T009',
            'tradeacco':'00999000000007006',
            'applyserial':'20150813003865',
            'tradesource':'000000060023'
        }
        return self.sendRequest('tradereq/withdraw', params)

    def userinfo(self):
        params = {
            'sessionkey':self.session,
            'function':'C010',
        }
        return self.sendRequest('account/getuserinfo', params)

    def workdatequery(self):
        params = {
            #'sessionkey':self.session,
            'function':'S008',
        }
        return self.sendRequest('query/workdatequery', params);

    def netvaluequery(self):
        params = {
            'sessionkey':self.session,
            'function':'S007',
        }
        return self.sendRequest('query/netvaluequery', params)

    def marketquery(self):
        params = {
            'fundcode':self.FUND_CODE,
            'startdate':'20141212',
            'enddate':'20150909',
            'pageno':'1',
            'applyrecordno':'100',
            'function':'S006',
        }
        return self.sendRequest('query/marketquery', params)

    #def fundlist(self):
    #    params = {
    #        'function':'T001',
    #    }
    #    return self.sendRequest('tradereq/fundlist', params)

    def supermoneyquery(self):
        params = {
            'sessionkey':self.session,
            'function':'S052' 
        }
        return self.sendRequest('query/supermoneyquery', params)
    
    def supermoneyprofit(self):
        params = {
            'startdate':'20150101',
            'enddate':'20151231',
            'sessionkey':self.session,
            'querytype':'0',
            'applyrecordno':'100',
            'function':'S054'
        }
        return self.sendRequest('query/supermoneyprofit', params)

    def supermoneytradequery(self):
        params = {
            'sessionkey':self.session,
            #'comefrom':'000000150001',
            'function':'S056'
        }
        return self.sendRequest('query/supermoneytradequery', params)

    def supermoneymarket(self):
        params = {
            'sessionkey':self.session,
            'startdate':'20150101',
            'enddate':'20151231',
            'applyrecordno':'100',
            'function':'S059'
        }
        return self.sendRequest('query/supermoneymarket', params)

    def supermoneypurchase(self):
        params = {
            'sessionkey':self.session,
            'tradeacco':self.TRADE_ACCOUNT,
            'tradepassword': self.TEST_PASSWORD,
            'fundcode':self.FUND_CODE,
            'sharetype':'A',
            'transfermoney':'1',
            'applysum':'33', 
            'businflag':'022',
            'busintype':'02',
            'tradesource':'000000150001',
            'detailcapitalmode':'01',
            'function':'T003',
        }
        return self.sendRequest('tradereq/purchase', params)
    
    def supermoneypurchaseNoPasswd(self):
        params = {
            'sessionkey':self.session,
            'tradeacco':self.TRADE_ACCOUNT,
            'fundcode':self.FUND_CODE,
            'sharetype':'A',
            'transfermoney':'1',
            'applysum':'3', 
            'businflag':'022',
            'busintype':'02',
            'tradesource':'000000150001',
            'detailcapitalmode':'01',
            'function':'T003',
        }
        return self.sendRequest('tradereq/purchase', params)

    def supermoneysale(self):
        params = {
            'sessionkey':self.session,
            'applysum':'50',
            'tradeacco':self.TRADE_ACCOUNT,
            'fundcode':self.FUND_CODE,
            'saleway':'4',
            'busintype':'02',
            'realtimeflag':'1',#1快速赎回, 0普通赎回
            'transfermoney': '0',#不起效果
            'tradesource':'000000150001',
            'sharetype':'A',
            'function':'T006'
        }
        return self.sendRequest('tradereq/sale', params)

    def logout(self):
        params = {
            'sessionkey':self.session,
            'function':'P004'
        }
        return self.sendRequest('integrate/logout', params)
  
    def realtimetransfer(self):
        params = {
            'function' : 'T020',
            'sessionkey' : self.session,
            'tradeacco' : self.TRADE_ACCOUNT,
            'fundcode' : self.FUND_CODE,
            'targetfundacco' : '4C1000000138',
            'targettradeacco' : '00000158',
            'transfertype' : '2', #投资人过户给投资人
            'transfermoney' : '0',
            'applysum' : '17',
            'sharetype' : 'A', 
            'capitalmode' : 'M', 
        }
        return self.sendRequest('tradereq/realtimetransfer', params)

class CountDownLatch:
    def __init__(self, count=1):
        self.count = count
        self.lock = threading.Condition()

    def countDown(self):
        self.lock.acquire()
        self.count -= 1
        if self.count <= 0:
            self.lock.notifyAll()
        self.lock.release()

    def await(self):
        self.lock.acquire()
        while self.count > 0:
            self.lock.wait()
        self.lock.release()


class WorkerThread(threading.Thread):
    latch = None
    count = 0
    method = None #method to test

    def __init__(self, latch, count, method):
        threading.Thread.__init__(self)
        self.latch = latch
        self.count = count
        self.method = method

    def run(self):
        api = FundApi()
        api.login()
        while self.count > 0:
            getattr(api, self.method)()
            self.count -= 1
        api.logout()
        self.latch.countDown()

class MyPrettyPrinter(pprint.PrettyPrinter):
    def format(self, object, context, maxlevels, level):
        if isinstance(object, unicode):
            return (object.encode('utf8'), True, False)
        return pprint.PrettyPrinter.format(self, object, context, maxlevels, level)


THREAD_COUNT = 10
EXEC_COUNT = 10

def performanceTest(method):
    start = int(time.time())
    threadCount = THREAD_COUNT
    latch = CountDownLatch(threadCount)
    while threadCount > 0:
        task = WorkerThread(latch, EXEC_COUNT, method)
        task.setDaemon(True)
        task.start()
        threadCount -= 1
    latch.await()
    end = int(time.time())
    elapsed = end - start
    print ('%d threads, %d call on method %s of each thread. cost time:%d seconds, QPS is %d') % (THREAD_COUNT, EXEC_COUNT, method, elapsed, THREAD_COUNT * EXEC_COUNT / elapsed)

def functionTest(method):
    pp = MyPrettyPrinter()
    api = FundApi()
    #api.loginNoPasswd()
    api.login()
    pp.pprint(getattr(api, method)())
    api.logout()

def transferMoneyTest():
    pp = MyPrettyPrinter()
    api = FundApi()
    api.loginNoPasswd()
    pp.pprint(api.supermoneypurchaseNoPasswd()) #fail 超级现金宝不支持无密下单
    api.logout()

if __name__ == '__main__':
    api = FundApi()
    pp = MyPrettyPrinter()
    #pp.pprint(api.workdatequery())
    print api.fundlist()
    #print api.ccbsignrecordquery()
    #print api.genopendata_allinpay()
    #print api.genopendata_icbc()
    #pp.pprint(api.genopendata_chinapay())
    #pp.pprint(api.chinapaymobileopenaccoorder())
    #print api.meropenacco()
    #functionTest('valubankquery')
    #functionTest('valutradechange')
    #functionTest('targetfunds')
    #transferMoneyTest()
    #functionTest('tradeappquery')
    #functionTest('supermoneyprofit') #ok ttzryebincomecurrent union thtzryebincomecurrent
    #functionTest('supermoneymarket') #ok tyebfundday
    #functionTest('querytradeacco')
    #functionTest('purchase') #ok
    #functionTest('sale') #ok
    #functionTest('sharequery') #ok
    #functionTest('supermoneyquery') #ok tyebasset 
    #functionTest('supermoneypurchase') #ok 可以指定transfermoney是否代扣款
    #functionTest('supermoneysale') #fail 不能控制是否划款
    #functionTest('supermoneytradequery') #fail tyebpayinrequest union tyebpayoutrequest (赎回applyshare都是0)
    #functionTest('withdraw')
    #functionTest('gift')
    #functionTest('realtimetransfer')
    #print api.queryrisk()
    sys.exit()
    #methods = ['sharequery', 'marketquery', 'tradeappquery', 'bonusquery', 'withdrawlist', 'hisprofitquery', 'workdatequery', 'netvaluequery']
    methods = ['purchase']
    for method in methods:
        performanceTest(method)
