# -*- coding: utf-8 -*- # !/usr/bin/env python """ 将重复的用户按组归并 """ import datetime import os import sys import mongoengine from bson import ObjectId from mongoengine import StringField, ReferenceField, IntField, DateTimeField, BooleanField, EmbeddedDocument from typing import Union PROJECT_ROOT = os.path.join(os.path.abspath(os.path.split(os.path.realpath(__file__))[0] + "/.."), '..') sys.path.insert(0, PROJECT_ROOT) import os os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'configs.production') from script.base import init_env init_env(interactive = False) from apps.web.agent.define import AGENT_INCOME_TYPE from apps.web.core.db import MonetaryField, Searchable from apps.web.common.models import WithdrawRecord, Balance from apps.web.constant import Const from apps.web.core import ROLE from apps.web.common.transaction import WithdrawStatus, WITHDRAW_PAY_TYPE from apps.web.agent.models import Agent from apps.web.core.models import WechatPayApp, AliApp, BankCard from apilib.monetary import Ratio, RMB from apps.web.dealer.define import DEALER_INCOME_TYPE from apps.web.dealer.models import Dealer, Merchant from apps.web.core.payment.wechat import WechatPaymentGateway class WechatAppOld(EmbeddedDocument): meta = { 'abstract': True } @property def occupant(self): return getattr(self, '__occupant__', None) @occupant.setter def occupant(self, occupant): setattr(self, '__occupant__', occupant) class AliAppOld(WechatAppOld): meta = {'abstract': True} @property def alipay_enable(self): return getattr(self, '__alipay_enable__') @alipay_enable.setter def aplipay_eable(self, alipay_enable): setattr(self, '__alipay_enable__', alipay_enable) class _WechatMiniApp(WechatAppOld): appid = StringField(verbose_name = 'appid', null = False) secret = StringField(verbose_name = "secretId", null = False, max_length = 32) mchid = StringField(verbose_name = 'mchid', null = False) apikey = StringField(verbose_name = 'apikey', null = False) sslcert_path = StringField(verbose_name = '证书pem格式', null = False) sslkey_path = StringField(verbose_name = '证书密钥pem格式', null = False) manual_withdraw = BooleanField(verbose_name = u'是否手动提现', default = False) def __repr__(self): return '' % (self.appid, self.mchid) def to_dict(self): return { 'appid': self.appid, 'secret': self.secret, 'mchid': self.mchid, 'apikey': self.apikey, 'sslcert_path': self.sslcert_path, 'sslkey_path': self.sslkey_path, 'manual_withdraw': self.manual_withdraw } @property def valid(self): return self.appid and self.secret and self.mchid and self.sslkey_path and self.sslcert_path and self.apikey @staticmethod def get_null_app(): return _WechatMiniApp(appid = '', secret = '', mchid = '', apikey = '', sslcert_path = '', sslkey_path = '') def __eq__(self, other): # type: (Union[WechatPayApp, _WechatPaymentApp, _WechatMiniApp])->bool if not isinstance(other, WechatPayApp) \ and not isinstance(other, _WechatPaymentApp) \ and isinstance(other, _WechatMiniApp): return False if other.appid != self.appid: return False if other.mchid != self.mchid: return False if other.secret != self.secret: return False if other.apikey != self.apikey: return False if other.sslcert_path != self.sslcert_path: return False if other.sslkey_path != self.sslkey_path: return False return True class _WechatPaymentApp(WechatAppOld): appid = StringField(verbose_name = 'appid', null = False) secret = StringField(verbose_name = "secretId", null = False, max_length = 32) mchid = StringField(verbose_name = 'mchid', null = False) apikey = StringField(verbose_name = 'apikey', null = False) sslcert_path = StringField(verbose_name = '证书pem格式', null = False) sslkey_path = StringField(verbose_name = '证书密钥pem格式', null = False) rootca_path = StringField(verbose_name = 'CA证书') manual_withdraw = BooleanField(verbose_name = u'是否手动提现', default = False) def __repr__(self): return '' % (self.appid,) def to_dict(self): def encrypt(url): return '******' if url else '' return { 'appid': self.appid, 'secret': self.secret, 'mchid': self.mchid, 'apikey': self.apikey, 'sslcert_path': self.sslcert_path, 'sslkey_path': self.sslkey_path, 'rootca_path': self.rootca_path, } @property def valid(self): return self.appid and self.secret and self.mchid and self.sslkey_path and self.sslcert_path and self.apikey @staticmethod def get_null_app(): return _WechatPaymentApp(appid = '', secret = '', mchid = '', apikey = '', sslcert_path = '', sslkey_path = '', rootca_path = '', manual_withdraw = False) def __eq__(self, other): # type: (Union[WechatPayApp, _WechatPaymentApp, _WechatMiniApp])->bool if not isinstance(other, WechatPayApp) \ and not isinstance(other, _WechatPaymentApp) \ and isinstance(other, _WechatMiniApp): return False if other.appid != self.appid: return False if other.mchid != self.mchid: return False if other.secret != self.secret: return False if other.apikey != self.apikey: return False if other.sslcert_path != self.sslcert_path: return False if other.sslkey_path != self.sslkey_path: return False return True class _AliPaymentApp(AliAppOld): appid = StringField(verbose_name = '支付宝appid', default = '') public_key_path = StringField(verbose_name = '支付宝账户公钥', default = '') alipayPublicKeyContent = StringField(verbose_name = '支付宝公钥文本') appPublicKeyContent = StringField(verbose_name = '应用公钥文本') app_private_key_path = StringField(verbose_name = '支付宝应用私钥', default = '') shadow = BooleanField(verbose_name = u'是否沙箱版本支付宝', default = False) def __repr__(self): return '' % (self.appid,) def to_dict(self): return { 'appid': self.appid, 'public_key_path': self.public_key_path, 'hasAlipayAppKeyPair': bool(self.app_private_key_path is not ''), 'alipayPublicKeyContent': self.alipayPublicKeyContent, 'appPublicKeyContent': self.appPublicKeyContent, 'app_private_key_path': self.app_private_key_path, 'debug': self.shadow } @staticmethod def get_null_app(): return _AliPaymentApp(appid = '', public_key_path = '', app_private_key_path = '', shadow = False) @property def valid(self): return self.appid and self.public_key_path and self.app_private_key_path @property def alipay_enable(self): return getattr(self, '__alipay_enable__') @alipay_enable.setter def aplipay_eable(self, alipay_enable): setattr(self, '__alipay_enable__', alipay_enable) def __eq__(self, other): # type: (Union[_AliPaymentApp, AliApp])->bool if not isinstance(other, _AliPaymentApp) and not isinstance(other, AliApp): return False if other.appid != self.appid: return False if other.app_private_key_path != self.app_private_key_path: return False if other.public_key_path != self.public_key_path: return False return True class AgentWithdrawRecord(Searchable): """ status: 0 <==> '已提交申请' 1 <==> '提现成功' 2 <==> '处理中' 3 <==> '提现失败' """ ownerId = StringField(verbose_name = "代理商id") phone = StringField(verbose_name = "持卡人电话", default = "") payType = StringField(verbose_name = "支付方式", default = "") agentName = StringField(verbose_name = "代理商姓名", default = "") cardUserName = StringField(verbose_name = "持卡人姓名", default = "") agentBalance = MonetaryField(verbose_name = "代理商当前余额", default = RMB('0.00')) accountCode = StringField(verbose_name = "提现银行卡号", default = "") order = StringField(verbose_name = "订单号", default = "") amount = MonetaryField(verbose_name = "提现金额", default = RMB('0.00')) serviceFee = MonetaryField(verbose_name = "服务费", default = RMB('0.00')) actualPay = MonetaryField(verbose_name = "实际金额", default = RMB('0.00')) withdrawBankCard = ReferenceField(BankCard) status = IntField(verbose_name = "状态", default = WithdrawStatus.SUBMITTED) remarks = StringField(verbose_name = "备注", default = "") postTime = DateTimeField(verbose_name = "请求时间", default = datetime.datetime.now) finishedTime = StringField(verbose_name = "完成时间", default = "") parentBankName = StringField(verbose_name = "银行名称", default = "") subBankName = StringField(verbose_name = "银行支行名称", default = "") meta = { 'indexes': ['ownerId'], 'collection': 'AgentWithdrawRecord', 'db_alias': 'default' } def do_error(): WithdrawRecord._meta.update({'auto_create_index': False}) for record in WithdrawRecord.objects.filter(order = None): # type: WithdrawRecord record.order = WithdrawRecord.make_no(ROLE.dealer, record.ownerId) record.withdrawGatewayKey = '' record.save() duplicate_records = WithdrawRecord.get_collection().aggregate([ {'$group': { '_id': {'firstField': "$order"}, 'uniqueIds': {'$addToSet': "$_id"}, 'count': {'$sum': 1} }}, {'$match': { 'count': {'$gt': 1} }} ], allowDiskUse = True) for duplicate_record in duplicate_records: if duplicate_record['count'] >= 2: print "duplicate ids = %s" % str(duplicate_record['uniqueIds']) WithdrawRecord.get_collection().remove( {'_id': {'$in': duplicate_record['uniqueIds'][0:duplicate_record['count'] - 1]}}) WithdrawRecord._meta.update({'auto_create_index': True}) def fill_agent(): def reactor_app(agent): __modified = False if agent.customizedWechatCashflowAllowable: wechat_app = getattr(agent, 'wechatPayApp', None) if not wechat_app: print 'configure error for no wechat. agent = {agentId}'.format(agentId = str(agent.id)) else: wechat_app = _WechatPaymentApp(**dict(wechat_app)) # type: _WechatPaymentApp if not wechat_app.valid: print 'configure error for invalid wechat. agent = {agentId}'.format(agentId = str(agent.id)) else: db_app = WechatPayApp.objects(appid = wechat_app.appid, mchid = wechat_app.mchid).first() # type: WechatPayApp if not db_app: pay_app = WechatPayApp(**wechat_app.to_dict()).save() # type: WechatPayApp agent.payAppWechat = pay_app __modified = True elif wechat_app == db_app: agent.payAppWechat = db_app __modified = True else: print 'configure error for wechat not equal db. appid = {appid}, mchid = {mchid}'.format( appid = wechat_app.appid, mchid = wechat_app.mchid) else: # print 'not customize wechat app. id = %s' % str(agent.id) return if agent.customizedAlipayCashflowAllowable: ali_app = getattr(agent, 'aliPayApp', None) if not ali_app: print 'configure error for no ali. agent = {agentId}'.format(agentId = str(agent.id)) else: ali_app = _AliPaymentApp(**dict(ali_app)) # type: _AliPaymentApp if not ali_app.valid: print 'configure error for invalid ali. agent = {agentId}'.format(agentId = str(agent.id)) else: db_app = AliApp.objects(appid = ali_app.appid).first() # type: AliApp if not db_app: pay_app = AliApp(appid = ali_app.appid, public_key_path = ali_app.public_key_path, alipayPublicKeyContent = ali_app.alipayPublicKeyContent, appPublicKeyContent = ali_app.appPublicKeyContent, app_private_key_path = ali_app.app_private_key_path, shadow = ali_app.shadow).save() # type: AliApp agent.payAppAli = pay_app __modified = True elif ali_app == db_app: agent.payAppAli = db_app __modified = True else: print 'configure error for ali not equal db. ali appid = {appid}'.format(appid = ali_app.appid) def reactor_pay_openid(agent): payOpenId = getattr(agent, 'payOpenId', '') payAppId = getattr(agent, 'payAppId', '') if payAppId and payOpenId: agent.set_bound_pay_openid(payAppId, openId = payOpenId) def reactor_balance(agent): try: custom_gateway_key = WechatPaymentGateway(agent.wechat_agent_pay_app).key platform_gateway_key = WechatPaymentGateway(agent.platform_wechat_pay_app).key agent.adBalance = {platform_gateway_key: Balance(balance = RMB(0), frozenBalance = RMB(0))} agent.trafficBalance = {platform_gateway_key: Balance(balance = RMB(0), frozenBalance = RMB(0))} agent.deviceBalance = {custom_gateway_key: Balance(balance = RMB(0), frozenBalance = RMB(0))} agent.withdrawBalance = {custom_gateway_key: Balance(balance = RMB(0), frozenBalance = RMB(0))} balance = RMB(getattr(agent, 'balance', '0.00')) if balance <= RMB('0.00'): return ad = RMB(getattr(agent.incomeMap, 'ad', 0)) sim = RMB(getattr(agent.incomeMap, 'dealer_card_fee', 0)) withdraw = RMB(getattr(agent.incomeMap, 'dealer_withdraw_fee', 0)) device = RMB(getattr(agent.incomeMap, 'dealer_device_fee', 0)) if ad > RMB(0) and sim == RMB(0) and withdraw == RMB(0) and device == RMB(0): agent.adBalance = {platform_gateway_key: Balance(balance = balance, frozenBalance = RMB(0))} return if sim > RMB(0) and ad == RMB(0) and withdraw == RMB(0) and device == RMB(0): agent.trafficBalance = {platform_gateway_key: Balance(balance = balance, frozenBalance = RMB(0))} return if withdraw > RMB(0) and sim == RMB(0) and ad == RMB(0) and device == RMB(0): agent.withdrawBalance = {custom_gateway_key: Balance(balance = balance, frozenBalance = RMB(0))} return if device > RMB(0) and sim == RMB(0) and withdraw == RMB(0) and ad == RMB(0): agent.deviceBalance = {custom_gateway_key: Balance(balance = balance, frozenBalance = RMB(0))} return agent.withdrawBalance = {custom_gateway_key: Balance(balance = balance, frozenBalance = RMB(0))} return except Exception, e: print 'exception = %s. id = %s' % (str(e), str(agent.id)) agents = [agent for agent in Agent.objects.all()] for agent in agents: reactor_app(agent) reactor_balance(agent) reactor_pay_openid(agent) agent.save() def rename_dealer_withdraw_record(): WithdrawRecord.get_collection().update({}, {'$set': {'role': ROLE.dealer, 'withdrawGatewayKey': ''}, '$unset': {'finishedTime': None}, '$rename': {"dealerName": "name", "dealerBalance": "balance", "userTel": "phone"}}, upsert = False, multi = True) def fill_agent_withdraw_reord(): records = [record for record in AgentWithdrawRecord.objects.all()] for record in records: fund_map = { 'amount': RMB(record.amount).mongo_amount, 'serviceFee': RMB(record.amount).mongo_amount, 'actualPay': RMB(record.actualPay).mongo_amount } agent = Agent.objects(id = str(record.ownerId)).first() if not agent: print 'agent is not exist. id = %s' % (str(agent.id)) continue withdraw_gateway = WechatPaymentGateway(agent.wechat_agent_pay_app) if record.payType not in [WITHDRAW_PAY_TYPE.WECHAT, WITHDRAW_PAY_TYPE.BANK]: print 'pay type is invalid. id = %s' % (str(agent.id)) continue pay_entity = BankCard( cardNo = record.accountCode, holderName = record.cardUserName, bankName = record.parentBankName, branchName = record.subBankName ) while True: try: WithdrawRecord.create(agent, withdraw_gateway, pay_entity, AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, record.payType, fund_map, False, False, **{ 'balance': record.agentBalance, 'withdrawFeeRatio': Ratio(Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO), # 'order': record.order, 'ownerId': record.ownerId, 'name': record.agentName, 'phone': record.phone, 'partition': [], 'status': record.status, 'postTime': record.postTime }) break except mongoengine.errors.NotUniqueError: print 'unique error' except Exception, e: print 'create agent withdraw record exception = %s; agentId = %s' % (e, str(agent.id)) def fill_dealer_withdraw_record(): records = WithdrawRecord.objects.filter(role = ROLE.dealer) for record in records: # type: WithdrawRecord if not record.ownerId: print 'has no dealer id. record = %s' % str(record.id) dealer = None # type: Dealer else: dealer = Dealer.objects(id = str(record.ownerId)).first() # type: Dealer if not dealer: print 'dealer is none. id = %s' % record.ownerId else: try: app = dealer_withdraw_device_app(dealer) if app: record.withdrawGatewayKey = WechatPaymentGateway(app).key except Exception, e: print 'dealer not belong to agent. dealerId = %s' % str(dealer.id) record.incomeType = DEALER_INCOME_TYPE.DEVICE_INCOME record.finishedTime = record.postTime extras = {} merchantId = getattr(record, 'merchantId', None) # 没有payType的时候, 记录merchantId; 有payType, accountCode记录银行卡卡号或者微信openid payType = getattr(record, 'payType', '') if not payType: if merchantId: payType = WITHDRAW_PAY_TYPE.BANK extras['withdrawBankCard'] = ObjectId(merchantId) bank_card = Merchant.objects(id = str(merchantId)).first() # type: Merchant if not bank_card: print 'has not no account code. record = %s' % str(record.id) else: if not record.accountCode: record.accountCode = bank_card.accountCode if not record.cardUserName: if dealer: record.cardUserName = dealer.nickname if not record.cardUserName: record.cardUserName = bank_card.merchantName if not record.parentBankName: record.parentBankName = bank_card.parentBankName if not record.subBankName: record.subBankName = bank_card.subBankName else: payType = WITHDRAW_PAY_TYPE.WECHAT if not record.accountCode: if dealer: record.accountCode = getattr(dealer, 'payOpenId', '') if not record.cardUserName: if dealer: record.cardUserName = dealer.nickname else: record.cardUserName = u'我' if not record.parentBankName: record.parentBankName = u'微信' if not record.subBankName: record.subBankName = u'微信企业付款' record.payType = payType else: payType = getattr(record, 'payType', '') if payType != WITHDRAW_PAY_TYPE.WECHAT: if not record.accountCode: print 'has not no account code. record = %s' % str(record.id) else: bank_card = Merchant.objects(accountCode = record.accountCode).first() # type: Merchant if not bank_card: print 'bank card is null. bank = %s; record = %s' % (str(record.accountCode), str(record.id)) else: extras['withdrawBankCard'] = ObjectId(str(bank_card.id)) if not record.cardUserName: record.cardUserName = bank_card.merchantName if not record.parentBankName: record.parentBankName = bank_card.parentBankName if not record.subBankName: record.subBankName = bank_card.subBankName if not record.name or not record.phone: if dealer: record.name = dealer.nickname record.phone = dealer.username old_withdrawFee = int(getattr(record, 'withdrawRatio', 0)) if old_withdrawFee != 0: new_withdrawFeeRatio = Ratio(old_withdrawFee) else: if dealer: new_withdrawFeeRatio = Ratio(dealer.withdrawFeeRatio) else: new_withdrawFeeRatio = Ratio(Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO) # 计算提现收益分配 if new_withdrawFeeRatio > Ratio(Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO): if dealer: agent_fee_ratio = (new_withdrawFeeRatio - Ratio(Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO)) earned = record.amount * (agent_fee_ratio / Const.WITHDRAW_FEE_UNIT) partition = [ {'role': 'agent', 'id': dealer.agentId, 'ratio': agent_fee_ratio.mongo_amount, 'earned': earned.mongo_amount} ] record.partition = partition if record.payType == WITHDRAW_PAY_TYPE.WECHAT: if record.status == WithdrawStatus.FAILED: record.refunded = True record.status = WithdrawStatus.CLOSED elif record.status == WithdrawStatus.CLOSED and not record.refunded: record.refunded = True else: pass record.extras = extras record.save() def fill_dealer(): try: print 'unset role...' Dealer.get_collection().update_many({}, {'$unset': {'role': ''}}, upsert = False) print 'fill dealers...' dealers = Dealer.objects.all() for dealer in dealers: # type: Dealer try: origin_balance = getattr(dealer, 'balance', RMB('0.00')) # type: RMB app = dealer_withdraw_device_app(dealer) if not app: print 'no app. id = %s' % str(dealer.id) continue key = WechatPaymentGateway(app).key dealer.deviceBalance = { key: Balance(balance = origin_balance, frozenBalance = RMB('0.00')) } dealer.adBalance = { key: Balance(balance = RMB('0.00'), frozenBalance = RMB('0.00')) } payOpenId = getattr(dealer, 'payOpenId', '') payAppId = getattr(dealer, 'payAppId', '') if payOpenId and payAppId: dealer.set_bound_pay_openid(payAppId, openId = payOpenId) inHouseAppId = getattr(dealer, 'inHouseAppId', '') inHouseOpenId = getattr(dealer, 'inHouseOpenId', '') if inHouseAppId and inHouseOpenId: dealer.set_bound_pay_openid(inHouseAppId, openId = inHouseOpenId) dealer.save() except Exception, e: print 'exception = %s; dealer id = %s' % (str(e), str(dealer.id)) return dealers except Exception, e: print 'fill_dealer exception = %s' % str(e) def check_dealer(): dealers = Dealer.objects.all() for dealer in dealers: # type: Dealer old_balance = RMB(getattr(dealer, 'balance', '0.00')) new_total_balance = dealer.total_balance new_device_balance = dealer.sub_balance(DEALER_INCOME_TYPE.DEVICE_INCOME) assert old_balance <= new_total_balance, 'total balace error. id = %s' % (str(dealer.id)) assert old_balance <= new_device_balance, 'device balace error. id = %s' % (str(dealer.id)) new_gateway_keys = dealer.deviceBalance.keys() assert len(new_gateway_keys) < 2, 'len gateway key errror. id = %s' % (str(dealer.id)) if new_total_balance == RMB(0): assert len(new_gateway_keys) == 0, 'len gateway key errror2. id = %s' % (str(dealer.id)) else: assert len(new_gateway_keys) == 1, 'len gateway key errror2. id = %s' % (str(dealer.id)) if len(new_gateway_keys) == 1: app = get_dealer_pay_wechat_app(dealer) old_gateway_key = WechatPaymentGateway(app).key assert old_gateway_key == new_gateway_keys[0], 'len gateway key errror3. id = %s' % (str(dealer.id)) payOpenId = getattr(dealer, 'payOpenId', '') payAppId = getattr(dealer, 'payAppId', '') for appid, bound_info in dealer.payOpenIdMap.iteritems(): assert appid, 'pay open id error 1. id = %s' % (str(dealer.id)) assert bound_info, 'pay open id error 2. id = %s' % (str(dealer.id)) assert bound_info.openId, 'pay open id error 3. id = %s' % (str(dealer.id)) if payOpenId and payAppId: assert payAppId in dealer.payOpenIdMap, 'pay open id error 4. id = %s' % (str(dealer.id)) assert payOpenId == dealer.payOpenIdMap[payAppId].openId, 'pay open id error 4. id = %s' % (str(dealer.id)) inHouseAppId = getattr(dealer, 'inHouseAppId', '') inHouseOpenId = getattr(dealer, 'inHouseOpenId', '') if inHouseAppId and inHouseOpenId: assert inHouseAppId in dealer.payOpenIdMap, 'pay open id error 5. id = %s' % (str(dealer.id)) assert inHouseOpenId == dealer.payOpenIdMap[inHouseAppId].openId, 'pay open id error 6. id = %s' % (str(dealer.id)) def check_agent(): agents = Agent.objects.all() for agent in agents: # type: Agent old_balance = RMB(getattr(agent, 'balance', '0.00')) new_total_balance = agent.total_balance assert old_balance == new_total_balance, 'total balace error. id = %s' % (str(dealer.id)) def fill_dealer_withdraw_record_2(): records = [record for record in WithdrawRecord.objects.filter(__raw__ = {'payAgentId': {'$exists': False}})] for record in records: # type: WithdrawRecord if not record.ownerId: print 'has no dealer id. record = %s' % str(record.id) dealer = None # type: Dealer else: dealer = Dealer.objects(id = str(record.ownerId)).first() # type: Dealer if not dealer: print 'dealer is none. id = %s' % record.ownerId else: try: app = dealer_withdraw_device_app(dealer) if app: record.payAgentId = app.occupantId record.save() except Exception, e: print 'dealer not belong to agent. dealerId = %s' % str(dealer.id) try: # print 'fill_agent ...' # fill_agent() # # print 'fill_dealer ...' # fill_dealer() # # print 'do_error ...' # do_error() # # print 'rename_dealer_withdraw_record ...' # rename_dealer_withdraw_record() # # print 'fill_dealer_withdraw_record ...' # fill_dealer_withdraw_record() # # print 'fill_agent_withdraw_reord ...' # fill_agent_withdraw_reord() fill_dealer_withdraw_record_2() print 'success over' except Exception, e: print 'exception = %s' % str(e)