123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- """
- web.agent.models
- ~~~~~~~~~
- """
- import datetime
- import itertools
- import logging
- import urllib
- from collections import namedtuple
- import simplejson as json
- from bson.objectid import ObjectId
- from django.conf import settings
- from django.utils.module_loading import import_string
- from mongoengine import MapField, LazyReferenceField, IntField
- from mongoengine.errors import DoesNotExist
- from mongoengine.fields import (StringField, DictField, BooleanField, DateTimeField, EmbeddedDocumentField,
- ListField)
- from typing import Any, Dict, TYPE_CHECKING, Optional, cast
- from apilib.monetary import RMB, sum_rmb, Permillage, Percent
- from apps.web.agent.define import AgentConst, AGENT_INCOME_SOURCE, AGENT_INCOME_TYPE
- from apps.web.agent.errors import PrimaryAgentDoesNotExist
- from apps.web.common.models import WithdrawRecord, CapitalUser, Balance, WithdrawBankCard
- from apps.web.common.transaction import WITHDRAW_PAY_TYPE
- from apps.web.constant import Const, AppPlatformType, DEALER_CONSUMPTION_AGG_KIND, MoniAppStatus
- from apps.web.core import PayAppType, APP_KEY_DELIMITER, ROLE
- from apps.web.core.db import Searchable, MonetaryField, StrictDictField, PermillageField, PercentField
- from apps.web.core.exceptions import InvalidParameter, NoAgentFound, NoManagerFound, ServiceException
- from apps.web.core.messages.sms import agentWithdrawSMSProvider
- from apps.web.core.models import WechatManagerApp, WechatAuthApp, BoundOpenInfo, AliApp, WechatPayApp, \
- WechatUserManagerApp, WithdrawEntity, WechatUserSubscribeManagerApp, WechatDealerSubscribeManagerApp
- from apps.web.core.payment import WithdrawGateway
- from apps.web.device.models import DeviceType
- from apps.web.exceptions import UserServerException
- from apps.web.management.models import Manager
- logger = logging.getLogger(__name__)
- if TYPE_CHECKING:
- from pymongo.results import UpdateResult
- from apps.web.common.transaction import WithdrawHandler
- from apps.web.core import PayAppBase
- AgentDisclaimer = namedtuple("AgentDisclaimer", ["content", "version"])
- class Agent(CapitalUser):
- """
- 代理商, 管理经销商
- """
- INCOME_SOURCE_LIST = AGENT_INCOME_SOURCE.choices()
- INCOME_SOURCE_TO_TYPE = AgentConst.MAP_SOURCE_TO_TYPE
- INCOME_TYPE_LIST = AGENT_INCOME_TYPE.choices()
- INCOME_TYPE_TO_FIELD = AgentConst.MAP_TYPE_TO_FIELD
- #: 默认的收入频道的分布情况
- DEFAULT_INCOME_MAP = {
- AGENT_INCOME_SOURCE.AD: 0,
- AGENT_INCOME_SOURCE.DEALER_WITHDRAW_FEE: 0,
- AGENT_INCOME_SOURCE.DEALER_CARD_FEE: 0,
- AGENT_INCOME_SOURCE.DEALER_DEVICE_FEE: 0
- }
- #: 默认管理平台微信公众号授权用户信息
- DEFAULT_WECHAT_AUTH_USER_INFO = {
- 'avatar': '',
- 'sex': 0
- }
- DEFAULT_PAY_TYPE = {
- ROLE.agent: {
- AppPlatformType.ALIPAY: PayAppType.ALIPAY,
- AppPlatformType.WECHAT: PayAppType.WECHAT,
- },
- ROLE.dealer: {
- AppPlatformType.ALIPAY: PayAppType.ALIPAY,
- AppPlatformType.WECHAT: PayAppType.WECHAT,
- },
- ROLE.myuser: {
- AppPlatformType.ALIPAY: PayAppType.ALIPAY,
- AppPlatformType.WECHAT: PayAppType.WECHAT,
- }
- }
- DEFAULT_CHECKPOINT = {
- 'gerenzhongxin': False, 'yue': False, 'baogaolaoban': False, 'fukuan': False
- }
- #: 收入相关
- deviceBalance = MapField(field = EmbeddedDocumentField(Balance))
- trafficBalance = MapField(field = EmbeddedDocumentField(Balance))
- adBalance = MapField(field = EmbeddedDocumentField(Balance))
- withdrawBalance = MapField(field = EmbeddedDocumentField(Balance))
- apiQuotaBalance = MapField(field = EmbeddedDocumentField(Balance))
- disableAdBalance = MapField(field = EmbeddedDocumentField(Balance))
- incomeMap = DictField(verbose_name = '收入字典', default = DEFAULT_INCOME_MAP)
- aggregatedIncome = DictField(verbose_name = '累计收入,只增不减', default = DEFAULT_INCOME_MAP)
- #: 代理自定义自己的产品名称和logo
- productLogo = StringField(verbose_name = "产品logo", default = "")
- productName = StringField(verbose_name = "产品名称", default = "")
- #: 公众号相关
- gzhServiceQrcodeUrl = StringField(verbose_name = "公众号二维码", default = "", max_length = 256)
- gzhServiceLinkUrl = StringField(verbose_name = "公众号链接二维码", default = "")
- forceFollowGzh = BooleanField(verbose_name = "是否强制关注公众号", default = False)
- forceFollowGzhForDealer = StringField(verbose_name = u'经销商的强制关注开关',
- default = 'free') # never:此代理商下的经销商,永远不准强制关注,free:经销商自由允许关注
- title = StringField(verbose_name = u'公众号强制关注的时候,显示的title', default = u'为了充分保障您的支付权益,首次使用需要您关注服务公众号辅助使用设备。')
- desc = StringField(verbose_name = u'公众号的加粉描述', default = u'关注步骤:手指按在上面二维码上,弹出窗口后,点公众号关注后您再次扫描设备上二维码启动设备。')
- #: 用于客服的联系方式
- serviceName = StringField(verbose_name = "客服名称", default = "", max_length = 32)
- servicePhone = StringField(verbose_name = "客服电话", default = "", max_length = 32)
- serviceQrcodeUrl = StringField(verbose_name = "二维码图片链接", default = "", max_length = 256)
- #: 特性列表
- features = ListField(field = StringField(), verbose_name = u'支持的特性', default = [])
- customizedUserGzhAllowable = BooleanField(verbose_name = '用户自定义公众号', default = False)
- customizedUserSubGzhAllowable = BooleanField(verbose_name = '用户订阅通知自定义公众号', default = False)
- customizedDealerGzhAllowable = BooleanField(verbose_name = '经销商自定义公众号', default = False)
- customizedDealerSubGzhAllowable = BooleanField(verbose_name = '经销商订阅通知自定义公众号', default = False)
- managerId = StringField(verbose_name = "从属于哪个管理员", null = False)
- adShow = BooleanField(verbose_name = "广告收入显示与否的选项", default = False)
- # 厂商参与设备运营分成的商户比例 资金池模式
- managerProfitShare = PercentField(verbose_name='代理商设备运营分成比例', default = Percent('0.00'))
- # 厂商参与设备运营分成的商户比例 暂不支持,但是可以先预留
- managerMerProfitShare = PercentField(verbose_name='代理商设备运营分成比例 商户收款', default = Percent('0.00'))
- # 提现OPENID映射
- payOpenIdMap = MapField(EmbeddedDocumentField(BoundOpenInfo))
- #: 用于API接口
- openAPI = BooleanField(verbose_name = "是否支持openAPI", default = False)
- agentSign = StringField(verbose_name = '唯一签名用于API调用,agent的签名,用于核对对方的签名')
- mySign = StringField(verbose_name = '唯一签名用于API调用,我们的签名,用于请求对方URl,对方鉴权')
- domain = StringField(verbose_name = '域名调用URL')
- wechatLoginAuthApp = EmbeddedDocumentField(verbose_name = '授权APP', document_type = WechatAuthApp)
- oldWechatLoginAuthApp = EmbeddedDocumentField(verbose_name = '老的授权APP',
- document_type = WechatAuthApp, default = None)
- wechatUserManagerialApp = EmbeddedDocumentField(verbose_name = '用户管理APP', document_type = WechatUserManagerApp)
- wechatDealerManagerialApp = EmbeddedDocumentField(verbose_name = '经销商管理APP', document_type = WechatManagerApp)
- wechatUserSubscribeManagerApp = EmbeddedDocumentField(verbose_name = '用户订阅通知APP',
- document_type = WechatUserSubscribeManagerApp)
- wechatDealerSubscribeManagerApp = EmbeddedDocumentField(verbose_name = '经销商订阅通知APP',
- document_type = WechatDealerSubscribeManagerApp)
- # 支付APP配置
- customizedAlipayCashflowAllowable = BooleanField(verbose_name = '是否开启自主支付宝收款权限', default = False)
- payAppAli = LazyReferenceField(document_type = AliApp, default = None)
- customizedWechatCashflowAllowable = BooleanField(verbose_name = '是否开启微信自主收款权限', default = False)
- payAppWechat = LazyReferenceField(document_type = WechatPayApp, verbose_name = '微信支付APP(用于提现和支付)', default = None)
- featureToggles = DictField(verbose_name = '特性开关', default = {})
- # 厂商给代理商的流量卡成本价. 提交经销商的时候,经销商的默认价格从这个继承,代理商也可以更改
- annualTrafficCost = MonetaryField(verbose_name = '厂商给代理商的流量卡成本价', default = Const.PLATFORM_DEFAULT_TRAFFIC_COST)
- # 平台给厂商的流量卡成本. 添加厂商的时候设置, 添加代理商的时候从厂商配置继承.
- # 如果有更改,更改之前的代理商不会修改这个费用,如果修改需要直接修改
- trafficCardCost = MonetaryField(verbose_name = '平台给厂商的流量卡成本价', default = Const.PLATFORM_DEFAULT_TRAFFIC_COST)
- # 厂商给代理商的提现费率. 代理商给经销商设置的提现费率必须大于该值. 配置资金池的代理商不受限制
- withdrawFeeRatio = PermillageField(verbose_name = '厂商给代理商的提现费率下限',
- default = Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO,
- min_value = Const.MIN_DEALER_WITHDRAW_FEE_RATIO,
- max_value = Const.MAX_DEALER_WITHDRAW_FEE_RATIO)
- # 平台给厂商的提现费率. 厂商给代理商的提现费率下限必须大于该值. 这个是平台成本价.
- withdrawFeeRatioCost = PermillageField(verbose_name = '平台给厂商的提现费率',
- default = Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO,
- min_value = Const.MIN_DEALER_WITHDRAW_FEE_RATIO,
- max_value = Const.MAX_DEALER_WITHDRAW_FEE_RATIO)
- bannerList = ListField(field = DictField(), verbose_name = "banner列表", default = [])
- #: 大部分情况下,是不允许直接变更支付网关的
- #: 手工给其转账的时候把该开关打开
- isChangingPaymentGateway = BooleanField(default = False)
- payType = DictField(verbose_name = u'各应用支付方式', default = DEFAULT_PAY_TYPE)
- moniAppList = ListField(verbose_name = u'监督公众号的清单', default = [])
- moniAppCheckPointDict = DictField(verbose_name = u'是否需要弹出监督公众号的代理商检查点', default = DEFAULT_CHECKPOINT)
- maxPayLimit = IntField(verbose_name = u'最大充值金额限制', default = 500)
- cardWechatInfo = DictField(verbose_name = u'批量导入实体卡绑定内勤微信账号信息', default = {})
- boundCardName = StringField(verbose_name = u'默认绑定卡主姓名', default = '')
- boundCardPhone = StringField(verbose_name = u'默认绑定卡主手机号', default = '')
- withdrawApps = MapField(EmbeddedDocumentField(document_type = WithdrawEntity), default = None)
- # 寻找 免责声明的 流程是 代理商--->主代理商--->系统代理商
- needDisclaimer = BooleanField(verbose_name = u"代理商是否需要免责声明", default = True)
- disclaimer = StringField(verbose_name = u"代理商设置的用户的免责声明", default = "")
- disclaimer_version = StringField(verbose_name = u"当前免责声明的版本,声明更新的时候须将版本号更新", default = "v1.0.0")
- dealerBankWithdrawFee = BooleanField(verbose_name = u'经销商提现到银行卡的手续费,计算方式', default = False)
- meta = {
- 'indexes': [
- {
- 'fields': ['username'],
- 'unique': True
- }
- ],
- "collection": "Agent",
- "db_alias": "default"
- }
- search_fields = ('username', 'nickname', 'openId', 'remarks')
- def __str__(self):
- return 'Agent<id={} username={} nickname={}>'.format(str(self.id), self.username, self.nickname)
- @property
- def service_phone(self):
- return self.servicePhone
- def my_pay_type(self, role, gateway_type):
- if role in self.payType and gateway_type in self.payType[role]:
- return self.payType[role][gateway_type]
- else:
- return self.DEFAULT_PAY_TYPE[role][gateway_type]
- @property
- def my_wechat_pay_app(self):
- # type: ()->WechatPayApp
- if self.customizedWechatCashflowAllowable:
- if not self.payAppWechat:
- raise Exception(u'第三方支付配置错误(1001)')
- else:
- my_app = self.payAppWechat.fetch() # type: WechatPayApp
- if not my_app.valid:
- raise Exception(u'第三方支付配置错误(1002)')
- else:
- if (str(self.id) == settings.MY_PRIMARY_AGENT_ID) and (not my_app.inhouse):
- raise Exception(u'第三方支付配置错误(1003)')
- my_app.occupantId = str(self.id)
- my_app.occupant = self
- return my_app
- my_app = WechatPayApp.get_null_app()
- my_app.occupantId = str(self.id)
- my_app.occupant = self
- return my_app
- @property
- def my_ali_pay_app(self):
- # type: ()->AliApp
- if self.customizedAlipayCashflowAllowable:
- if not self.payAppAli:
- raise Exception(u'第三方支付配置错误(1001)')
- else:
- my_app = self.payAppAli.fetch() # type: AliApp
- if not my_app.valid:
- raise Exception(u'第三方支付配置错误(1002)')
- else:
- if (str(self.id) == settings.MY_PRIMARY_AGENT_ID) and (not my_app.inhouse):
- raise Exception(u'第三方支付配置错误(1003)')
- my_app.occupantId = str(self.id)
- my_app.occupant = self
- return my_app
- my_app = AliApp.get_null_app()
- my_app.occupantId = str(self.id)
- my_app.occupant = self
- return my_app
- def to_dict(self, shadow = False):
- rv = super(Agent, self).to_dict()
- rv.update({
- 'id': str(self.id),
- 'productLogo': self.product_logo,
- 'productName': self.product_name,
- 'serviceName': self.serviceName,
- 'servicePhone': self.servicePhone,
- 'serviceQrcodeUrl': self.serviceQrcodeUrl,
- 'gzhServiceQrcodeUrl': self.gzhServiceQrcodeUrl,
- 'adShow': self.adShow,
- 'forceFollowGzh': self.forceFollowGzh,
- 'managerId': self.managerId,
- 'isPrimary': self.is_primary,
- 'featureList': self.feature_list,
- 'annualTrafficCost': self.annualTrafficCost,
- 'trafficCardCost': self.trafficCardCost,
- 'title': self.title,
- 'desc': self.desc,
- 'smsVendor': self.smsVendor,
- 'dealerBankWithdrawFee': self.dealerBankWithdrawFee,
- 'bankWithdrawFee': self.bankWithdrawFee
- })
- return rv
- @classmethod
- def filter(cls, manager_id, search_key, page_index, page_size, moniAppId = None, shadow = False):
- query = {'managerId': manager_id} if manager_id is not None else {}
- if moniAppId:
- query.update({'moniAppList': moniAppId})
- agents = cls.objects(**query).search(search_key).order_by('-dateTimeAdded')
- total = agents.count()
- dataList = []
- for agent in agents.paginate(pageIndex = page_index, pageSize = page_size): # type: Agent
- deviceTypes = DeviceType.objects(agentId = str(agent.id)).all()
- if len(deviceTypes):
- devTypeList = [_.to_dict() for _ in deviceTypes]
- else:
- devTypeList = []
- if Manager.objects(primeAgentId = str(agent.id)).first():
- isPrimary = True
- else:
- isPrimary = False
- zhejiangDict = {}
- try:
- from apps.web.south_intf.zhejiang_fire import ZhejiangNorther
- obj = ZhejiangNorther.objects.get(tokenId = str(agent.id))
- ip, port = obj.northPort.split(":")
- zhejiangDict = {
- 'loginUsername': obj.usernameFromHear,
- 'loginPassword': obj.passwordFromHear,
- 'mqUsername': obj.mqUser,
- 'mqPassword': obj.mqPassword,
- 'code': obj.serviceCodeFromNorth,
- "northIp": ip,
- "northPort": port
- }
- except Exception as e:
- logger.error("agent filter got an error = {}".format(e))
- pass
- item = {
- 'id': str(agent.id),
- 'nickname': agent.nickname,
- 'username': '******' if shadow else agent.username,
- 'annualTrafficCost': agent.annualTrafficCost,
- 'trafficCardCost': agent.trafficCardCost,
- 'withdrawFeeRatio': float(agent.withdrawFeeRatio.amount),
- 'withdrawFeeRatioCost': float(agent.withdrawFeeRatioCost.amount),
- 'managerProfitShare': float(agent.managerProfitShare.amount),
- 'dealerTotal': agent.get_dealers().count(),
- 'dateTimeAdded': agent.dateTimeAdded,
- 'featureList': agent.feature_list,
- 'bannerImgList': agent.bannerList,
- 'customizedAlipayCashflowAllowable': agent.customizedAlipayCashflowAllowable,
- 'customizedWechatCashflowAllowable': agent.customizedWechatCashflowAllowable,
- 'customizedDealerGzhAllowable': agent.customizedDealerGzhAllowable,
- 'customizedDealerSubGzhAllowable': agent.customizedDealerSubGzhAllowable,
- 'customizedUserGzhAllowable': agent.customizedUserGzhAllowable,
- 'customizedUserSubGzhAllowable': agent.customizedUserSubGzhAllowable,
- 'isPrimary': isPrimary,
- 'deviceType': devTypeList,
- 'detail': {
- 'agentId': str(agent.id),
- 'managerId': agent.managerId,
- 'features': json.dumps(agent.feature_boolean_map)
- },
- 'ZJFirePlatform': zhejiangDict,
- 'forceFollowGzh': 'yes' if agent.forceFollowGzh else 'no',
- 'forceFollowGzhForDealer': agent.forceFollowGzhForDealer,
- 'productName': agent.productName,
- 'maxPayLimit': agent.maxPayLimit
- }
- try:
- pay_app_ali = agent.my_ali_pay_app # type: Optional[AliApp]
- except Exception as e:
- pay_app_ali = None
- if pay_app_ali: # type
- item.update({'aliPayApp': pay_app_ali.to_dict()}) # type: AliApp
- else:
- item.update({'aliPayApp': AliApp.get_null_app().to_dict()})
- try:
- pay_app_wechat = agent.my_wechat_pay_app
- except Exception as e:
- pay_app_wechat = None
- if pay_app_wechat:
- item.update({'wechatPayApp': pay_app_wechat.to_dict()})
- else:
- item.update({'wechatPayApp': WechatPayApp.get_null_app().to_dict()})
- dealer_manager_app = agent.wechatDealerManagerialApp or WechatManagerApp.get_null_app() # type: WechatManagerApp
- item.update({'dealer': dealer_manager_app.to_dict()})
- user_manager_app = agent.wechatUserManagerialApp or WechatUserManagerApp.get_null_app() # type: WechatUserManagerApp
- item.update({'user': user_manager_app.to_dict()})
- user_sub_manager_app = agent.wechatUserSubscribeManagerApp or WechatUserSubscribeManagerApp.get_null_app() # type: WechatUserSubscribeManagerApp
- item.update({'user_sub': user_sub_manager_app.to_dict()})
- dealer_sub_manager_app = agent.wechatDealerSubscribeManagerApp or WechatDealerSubscribeManagerApp.get_null_app() # type: WechatDealerSubscribeManagerApp
- item.update({'dealer_sub': dealer_sub_manager_app.to_dict()})
- # 监督号的配置
- moniAppList = []
- for appId in agent.moniAppList:
- try:
- moniApp = MoniApp.objects.get(appId = appId)
- except Exception, e:
- continue
- moniAppList.append({'appId': moniApp.appId, 'appName': moniApp.appName})
- item.update({'moniApps': moniAppList})
- pointList = []
- for key, switch in agent.moniAppCheckPointDict.items():
- try:
- point = MoniAppPoint.objects.get(key = key)
- except Exception, e:
- continue
- pointList.append({'name': point.name, 'key': key, 'switch': switch})
- item.update({'pointDict': pointList})
- if agent.is_primary:
- item.update({'isPrimary': True})
- dataList.insert(0, item)
- else:
- item.update({'isPrimary': False})
- dataList.append(item)
- return total, dataList
- @property
- def product_logo(self):
- if not self.productLogo:
- return self.primary_agent.productLogo
- else:
- return self.productLogo
- @property
- def product_name(self):
- if not self.productName:
- return self.primary_agent.productName
- else:
- return self.productName
- @staticmethod
- def get_agent(agentId):
- try:
- agent = Agent.objects.get(id = ObjectId(agentId)) # type: Agent
- return agent.to_dict()
- except DoesNotExist:
- logger.exception('can not find agent from db,id=%s' % agentId)
- return None
- def update(self, **kwargs):
- # type: (**Any)->int
- """
- 每次更新的时候确保正在更改支付网关的选项设置为关闭
- :param kwargs:
- :return:
- """
- return super(Agent, self).update(**kwargs)
- @staticmethod
- def update_agent(agentId, valueDict):
- """TODO REFACTOR"""
- if not valueDict:
- return True
- try:
- agent = Agent.objects(id = agentId).get()
- updated = agent.update(**valueDict)
- assert updated, u'更新失败'
- except Exception as e:
- logger.exception('update agent error=%s' % e)
- return False
- return True
- @staticmethod
- def record_cookie(agentId, response):
- agent = Agent.get_agent(agentId)
- if agent is None:
- return response
- response.set_cookie(key = 'agentLogoUrl',
- value = agent['productLogo'],
- max_age = 24 * 3600 * 30,
- domain = settings.COOKIE_DOMAIN)
- pn = urllib.quote(agent['productName'].encode('utf-8'))
- response.set_cookie(key = 'agentBrandName', value = pn, max_age = 24 * 3600 * 30,
- domain = settings.COOKIE_DOMAIN)
- response.set_cookie(key = 'agentId', value = agentId, max_age = 24 * 3600 * 30, domain = settings.COOKIE_DOMAIN)
- return response
- def put_cookie(self, response):
- logo = self.product_logo
- if not logo:
- logo = '/app/img/logo.png'
- response.set_cookie(key = 'agentLogoUrl', value = logo,
- max_age = 24 * 3600 * 30,
- domain = settings.COOKIE_DOMAIN)
- productName = self.product_name
- response.set_cookie(key = 'agentBrandName',
- value = urllib.quote(productName.encode('utf-8')),
- max_age = 24 * 3600 * 30,
- domain = settings.COOKIE_DOMAIN)
- response.set_cookie(key = 'agentId',
- value = str(self.id),
- max_age = 24 * 3600 * 30,
- domain = settings.COOKIE_DOMAIN)
- return response
- def get_dealers(self):
- """
- 获取旗下经销商
- :return:
- """
- Dealer = import_string('apps.web.dealer.models.Dealer')
- return Dealer.objects(agentId = str(self.id))
- @property
- def primary_agent_id(self):
- return self.manager.primeAgentId
- @property
- def manager(self): # type:() -> Manager
- if not hasattr(self, '__manager__'):
- manager = Manager.objects(id=self.managerId).first()
- if not manager:
- raise NoManagerFound(manager_id=self.managerId)
- setattr(self, '__manager__', manager)
- return getattr(self, '__manager__')
- @property
- def primary_agent(self): # type:() -> Agent
- if not hasattr(self, '__my_prime_agent__'):
- primeAgent = Agent.objects(id = self.primary_agent_id).first()
- if not primeAgent:
- raise NoAgentFound(agent_id = self.primary_agent_id)
- setattr(self, '__my_prime_agent__', primeAgent)
- return getattr(self, '__my_prime_agent__')
- @property
- def is_primary(self):
- """
- 不需要去多查询一次数据库 只需要找到manager即可
- """
- return self.is_equal(self.primary_agent)
- @property
- def is_in_domain(self):
- try:
- return self.manager.domain == settings.MY_DOMAIN
- except Exception as e:
- logger.error('get manager failure. error = %s; id = %s' % (str(e), str(self.id)))
- return False
- @classmethod
- def from_agent(cls, agent, app_type, **kwargs):
- param_key = '{app_type}_app'.format(app_type = app_type)
- attr_or_func = getattr(agent, param_key)
- if hasattr(attr_or_func, '__call__'):
- return attr_or_func(**kwargs)
- else:
- return attr_or_func
- @classmethod
- def factory(cls, **kwargs):
- factory_type = kwargs.pop('factory_type')
- if factory_type == 'app':
- app_type = kwargs.pop('app_type')
- return lambda agent: cls.from_agent(agent = agent, app_type = app_type, **kwargs)
- elif factory_type == 'withdraw_source_key':
- pay_app = kwargs.pop('pay_app')
- return lambda agent: getattr(agent, 'withdraw_source_key')(pay_app)
- else:
- raise InvalidParameter(u'参数错误')
- @staticmethod
- def get_inhouse_prime_agent():
- # type: ()->Agent
- """
- 为了方便识别,如果获取默认代理商失败,需要报错为默认代理商找不到
- """
- try:
- return Agent.objects(id = str(Agent.inhouse_prime_agent_id())).get()
- except DoesNotExist:
- raise PrimaryAgentDoesNotExist('failed to get default primary agent')
- @property
- def inhouse_prime_agent(self):
- if str(self.id) == str(self.inhouse_prime_agent_id()):
- return self
- else:
- return Agent.get_inhouse_prime_agent()
- @staticmethod
- def inhouse_prime_agent_id():
- return str(settings.MY_PRIMARY_AGENT_ID)
- @property
- def customizedCashflowAllowable(self):
- """
- 目前withdrawSourceKey是使用微信商户来标记. 所以资金池场景下仅
- 判断微信商户是否支持就可以。在资金池场景下, 必须配置微信支付
- :return:
- """
- return self.customizedWechatCashflowAllowable
- def wechat_env_pay_app(self, role = None, pay_app_type = None):
- # type: (Optional[None, str], Optional[None, str])->Optional[cast(PayAppBase)]
- assert not (role and pay_app_type), 'role and app_type must not have value in the same time.'
- if not self.customizedCashflowAllowable:
- if pay_app_type:
- _pay_app_type = pay_app_type
- _role = None
- else:
- custom = self.payType.get('custom', False)
- if custom:
- if role in self.payType and AppPlatformType.WECHAT in self.payType[role]:
- _pay_app_type = self.payType[role][AppPlatformType.WECHAT]
- _role = None
- else:
- _pay_app_type = None
- _role = role
- else:
- _pay_app_type = None
- _role = role
- primary_agent = self.primary_agent
- if self.is_equal(primary_agent):
- return self.inhouse_prime_agent.wechat_env_pay_app(_role, _pay_app_type)
- else:
- return primary_agent.wechat_env_pay_app(_role, _pay_app_type)
- if role:
- app_pay_type = self.my_pay_type(role, AppPlatformType.WECHAT)
- else:
- app_pay_type = pay_app_type
- if app_pay_type == PayAppType.WECHAT:
- app = self.my_wechat_pay_app # type: WechatPayApp
- if not app.enable or not app.valid:
- raise Exception(u'系统配置错误(第三方支付)')
- return app
- raise Exception(u'系统配置错误(第三方支付)')
- def alipay_env_pay_app(self, role = None, pay_app_type = None):
- assert not (role and pay_app_type), 'role and app_type must not have value in the same time.'
- if not self.customizedCashflowAllowable:
- # 如果代理商配置了需要的支付类型, 以代理商配置的为准
- if pay_app_type:
- _pay_app_type = pay_app_type
- _role = None
- else:
- custom = self.payType.get('custom', False)
- if custom:
- if role in self.payType and AppPlatformType.ALIPAY in self.payType[role]:
- _pay_app_type = self.payType[role][AppPlatformType.ALIPAY]
- _role = None
- else:
- _pay_app_type = None
- _role = role
- else:
- _pay_app_type = None
- _role = role
- primary_agent = self.primary_agent
- if self.is_equal(primary_agent):
- return self.inhouse_prime_agent.alipay_env_pay_app(_role, _pay_app_type)
- else:
- return primary_agent.alipay_env_pay_app(_role, _pay_app_type)
- if role:
- app_pay_type = self.my_pay_type(role, AppPlatformType.ALIPAY)
- else:
- app_pay_type = pay_app_type
- if app_pay_type == PayAppType.ALIPAY:
- app = self.my_ali_pay_app # type: AliApp
- if not app.enable or not app.valid:
- raise Exception(u'系统配置错误(第三方支付)')
- return app
- raise Exception(u'系统配置错误(第三方支付)')
- def _check_wechat_withdraw(self):
- my_app = self.my_wechat_pay_app
- if not my_app.enable:
- raise Exception(u'系统配置错误(三方支付)')
- @property
- def my_wechat_withdraw_app(self):
- my_app = None
- if self.withdrawApps:
- source_key = self.withdraw_source_key()
- if source_key in self.withdrawApps:
- my_app = self.withdrawApps[source_key].wechat_app
- if not my_app:
- my_app = self.my_wechat_pay_app
- if (not my_app.valid) or (not my_app.enable):
- raise Exception(u'配置错误(第三方支付)')
- my_app.occupant = self
- my_app.occupantId = str(self.id)
- return my_app
- @property
- def wechat_withdraw_app(self):
- if not self.customizedCashflowAllowable:
- primary_agent = self.primary_agent
- if self.is_equal(primary_agent):
- return self.inhouse_prime_agent.wechat_withdraw_app
- else:
- return primary_agent.wechat_withdraw_app
- return self.my_wechat_withdraw_app
- def withdraw_source_key(self, pay_app = None):
- # type:(PayAppBase)->str
- if not pay_app:
- my_app = self.wechat_env_pay_app(pay_app_type = PayAppType.WECHAT)
- if (not my_app) or (not my_app.valid) or (not my_app.enable):
- raise Exception(u'配置错误(第三方支付)')
- else:
- agent = pay_app.occupant # type: Agent
- my_app = agent.my_wechat_pay_app # type: WechatPayApp
- if (not my_app) or (not my_app.valid) or (not my_app.enable):
- raise Exception(u'配置错误(第三方支付)')
- if hasattr(my_app, '__source_key__'):
- return APP_KEY_DELIMITER.join(
- [WithdrawGateway.LEDGER_PREFIX, my_app.pay_app_type, getattr(my_app, '__source_key__')])
- else:
- raise AttributeError('no __source_key__ attribute')
- # if my_app.inhouse:
- # return settings.MY_PRIMARY_AGENT_WALLET_KEY # 所有平台inhouse资金池全部使用固定的资金池KEY
- # else:
- # return APP_KEY_DELIMITER.join(
- # [WithdrawGateway.LEDGER_PREFIX, my_app.pay_app_type, getattr(my_app, '__source_key__')])
- @classmethod
- def _parse_source_key(cls, source_key):
- # type: (str)->tuple
- tokens = source_key.split(APP_KEY_DELIMITER)
- return True if tokens[0].startswith(WithdrawGateway.LEDGER_PREFIX) else False, tokens[1], tokens[2], tokens[3:]
- # if source_key == settings.MY_PRIMARY_AGENT_WALLET_KEY:
- # is_ledger, pay_app_type, occupant_id = True, PayAppType.WECHAT, settings.MY_PRIMARY_AGENT_WALLET_KEY
- # else:
- # tokens = source_key.split(APP_KEY_DELIMITER)
- # is_ledger, pay_app_type, occupant_id = True if tokens[0].startswith(
- # WithdrawGateway.LEDGER_PREFIX) else False, tokens[1], tokens[2]
- @property
- def disclaimerVersionFormat(self):
- return "{}_{}"
- @property
- def disclaimerVersion(self):
- verFormat = self.disclaimerVersionFormat
- return verFormat.format(str(self.id), self.disclaimer_version)
- @disclaimerVersion.setter
- def disclaimerVersion(self, value):
- try:
- self.update(disclaimer_version = value)
- except Exception as e:
- logger.error(
- "set disclaimer version error, agent is <{}>, version is <{}>, error is ".format(self, value, e))
- raise e
- @property
- def disclaimerContent(self):
- return self.disclaimer
- @disclaimerContent.setter
- def disclaimerContent(self, value):
- try:
- self.update(disclaimer = value)
- except Exception as e:
- logger.error("set disclaimer content error, agent is <{}>. error is <{}>".format(self, e))
- raise e
- @property
- def disclaimerAgent(self):
- """
- 获取设置了免责声明的代理商
- :return:
- """
- if self.disclaimer:
- return self
- primeAgent = self.primary_agent
- if primeAgent.disclaimer:
- return primeAgent
- inhousePrimeAgent = self.inhouse_prime_agent
- return inhousePrimeAgent
- @classmethod
- def get_disclaimer(cls, agentId):
- """
- 获取 代理商的 免责声明
- :param agentId:
- :return:
- """
- agent = cls.objects.get(id = agentId)
- if not agent.needDisclaimer:
- return AgentDisclaimer(content = "", version = agent.disclaimerVersion)
- agent = agent.disclaimerAgent
- content = agent.disclaimerContent
- disclaimer = AgentDisclaimer(content = content, version = agent.disclaimerVersion)
- return disclaimer
- @classmethod
- def withdraw_gateway_list(cls, source_key):
- is_ledger, pay_app_type, occupant_id, tokens = cls._parse_source_key(source_key)
- if not is_ledger:
- return is_ledger, None, {
- 'alipay': WithdrawGateway(AliApp.get_null_app()),
- 'wechat': WithdrawGateway(WechatPayApp.get_null_app()),
- 'wechatV3': WithdrawGateway(WechatPayApp.get_null_app())
- }
- agent = cls.objects(id = occupant_id).first()
- if not agent:
- raise UserServerException(u'系统配置错误(第三方支付),请联系平台客服(1002)')
- if pay_app_type != PayAppType.WECHAT:
- raise UserServerException(u'系统配置错误(第三方支付),请联系平台客服(1001)')
- if not agent.withdrawApps:
- return is_ledger, agent, {
- 'alipay': WithdrawGateway(AliApp.get_null_app()),
- 'wechat': agent.my_wechat_pay_app.new_withdraw_gateway(gateway_version = 'v1'),
- 'wechatV3': agent.my_wechat_pay_app.new_withdraw_gateway(gateway_version = 'v3')
- }
- else:
- if source_key not in agent.withdrawApps:
- raise UserServerException(u'系统配置错误(第三方支付),请联系平台客服(1003)')
- withdraw_entity = agent.withdrawApps[source_key] # type: WithdrawEntity
- pay_rv = {
- 'alipay': WithdrawGateway(AliApp.get_null_app())
- }
- if withdraw_entity.alipay_app:
- withdraw_entity.alipay_app.occupantId = str(agent.id)
- withdraw_entity.alipay_app.occupant = agent
- pay_rv['alipay'] = withdraw_entity.alipay_app.new_withdraw_gateway()
- # 微信提现必须配置, 接口可能不支持提现, 但是手工提现以微信为准
- withdraw_entity.wechat_app.occupantId = str(agent.id)
- withdraw_entity.wechat_app.occupant = agent
- pay_rv.update({
- 'wechat': withdraw_entity.wechat_app.new_withdraw_gateway(gateway_version = 'v1'),
- 'wechatV3': withdraw_entity.wechat_app.new_withdraw_gateway(gateway_version = 'v3')
- })
- return is_ledger, agent, pay_rv
- @staticmethod
- def get_platform_wechat_manager_app():
- agent = Agent.get_inhouse_prime_agent()
- return agent.platform_wechat_manager_app
- @property
- def platform_wechat_manager_app(self):
- agent = self.inhouse_prime_agent
- app = agent.wechatDealerManagerialApp
- if not app:
- raise Exception(u'公众号配置错误')
- app.occupantId = str(agent.id)
- return app
- @staticmethod
- def get_inhouse_wechat_user_manager_app():
- agent = Agent.get_inhouse_prime_agent()
- return agent.inhouse_wechat_user_manager_app
- @property
- def inhouse_wechat_user_manager_app(self):
- agent = self.inhouse_prime_agent
- app = agent.wechatUserManagerialApp
- if not app:
- raise Exception(u'公众号配置错误')
- app.occupantId = str(agent.id)
- return app
- @property
- def wechat_auth_app(self):
- """
- 标识用户只用平台的公众号对应APPID. 部分用户由于前期原因配置了自己的
- 该接口仅做兼容
- :return:
- """
- if self.wechatLoginAuthApp:
- app = self.wechatLoginAuthApp # type: WechatAuthApp
- app.occupantId = str(self.id)
- return app
- else:
- primary_agent = self.primary_agent
- if self.is_equal(primary_agent):
- return self.inhouse_prime_agent.wechat_auth_app
- else:
- return primary_agent.wechat_auth_app
- @property
- def wechat_user_manager_app(self):
- if self.customizedUserGzhAllowable:
- if self.wechatUserManagerialApp:
- app = self.wechatUserManagerialApp # type: WechatManagerApp
- app.occupantId = str(self.id)
- app.occupant = self
- return app
- else:
- raise Exception(u'系统配置错误(wechat_user_manager_app)')
- else:
- primary_agent = self.primary_agent
- if self.is_equal(primary_agent):
- inhouse_agent = self.inhouse_prime_agent
- if inhouse_agent.customizedUserGzhAllowable:
- return inhouse_agent.wechat_user_manager_app
- else:
- raise Exception(u'系统未配置(wechat_user_manager_app)')
- else:
- return primary_agent.wechat_user_manager_app
- @property
- def wechat_user_messager_app(self):
- if self.customizedUserGzhAllowable or self.customizedUserSubGzhAllowable:
- if self.customizedUserGzhAllowable:
- if self.wechatUserManagerialApp:
- app = self.wechatUserManagerialApp # type: WechatUserManagerApp
- app.occupantId = str(self.id)
- app.occupant = self
- return app
- else:
- raise Exception(u'系统配置错误(wechat_user_manager_app)')
- if self.customizedUserSubGzhAllowable:
- if self.wechatUserSubscribeManagerApp:
- app = self.wechatUserSubscribeManagerApp # type: WechatUserSubscribeManagerApp
- app.occupantId = str(self.id)
- app.occupant = self
- return app
- else:
- raise Exception(u'系统配置错误(wechat_user_subscribe_manager_app)')
- else:
- primary_agent = self.primary_agent
- if self.is_equal(primary_agent):
- inhouse_agent = self.inhouse_prime_agent
- if inhouse_agent.customizedUserGzhAllowable or inhouse_agent.customizedUserSubGzhAllowable:
- return inhouse_agent.wechat_user_messager_app
- else:
- raise Exception(u'系统未配置(wechat_user_manager_app|wechat_user_subscribe_manager_app)')
- else:
- return primary_agent.wechat_user_messager_app
- @property
- def wechat_manager_app(self):
- if self.customizedDealerGzhAllowable:
- if self.wechatDealerManagerialApp:
- app = self.wechatDealerManagerialApp # type: WechatManagerApp
- app.occupantId = str(self.id)
- return app
- else:
- raise Exception(u'系统配置错误(wechat_dealer_manager_app, %s)' % str(self.id))
- else:
- primary_agent = self.primary_agent
- if self.is_equal(primary_agent):
- return self.inhouse_prime_agent.wechat_manager_app
- else:
- return primary_agent.wechat_manager_app
- @property
- def wechat_user_subscribe_manager_app(self):
- if self.customizedUserSubGzhAllowable:
- if self.wechatUserSubscribeManagerApp:
- app = self.wechatUserSubscribeManagerApp # type: WechatManagerApp
- app.occupantId = str(self.id)
- app.occupant = self
- return app
- else:
- raise Exception(u'系统配置错误(wechat_user_subscribe_manager_app)')
- else:
- primary_agent = self.primary_agent
- if self.is_equal(primary_agent):
- inhouse_agent = self.inhouse_prime_agent
- if inhouse_agent.customizedUserSubGzhAllowable:
- return inhouse_agent.wechat_user_subscribe_manager_app
- else:
- raise Exception(u'系统未配置(wechat_user_subscribe_manager_app)')
- else:
- return primary_agent.wechat_user_subscribe_manager_app
- @property
- def wechat_dealer_subscribe_manager_app(self):
- if self.customizedDealerSubGzhAllowable:
- if self.wechatDealerSubscribeManagerApp:
- app = self.wechatDealerSubscribeManagerApp # type: WechatManagerApp
- app.occupantId = str(self.id)
- app.occupant = self
- return app
- else:
- raise Exception(u'系统配置错误(wechat_user_manager_app)')
- else:
- primary_agent = self.primary_agent
- if self.is_equal(primary_agent):
- return self.inhouse_prime_agent.wechatDealerSubscribeManagerApp
- else:
- return primary_agent.wechatDealerSubscribeManagerApp
- def get_user_sub_template_id_list(self):
- if not self.customizedUserSubGzhAllowable:
- return []
- if not self.wechatUserSubscribeManagerApp.templateIdMap:
- return []
- else:
- try:
- return map(lambda _: _['templateId'], self.wechatUserSubscribeManagerApp.templateIdMap.values())
- except Exception as e:
- logger.info('error e=<{}>'.format(e))
- return []
- def get_dealer_sub_template_id_list(self):
- if not self.customizedDealerSubGzhAllowable:
- return []
- if not self.wechatUserSubscribeManagerApp.templateIdMap:
- return []
- else:
- try:
- return map(lambda _: _['templateId'], self.wechatDealerSubscribeManagerApp.templateIdMap.values())
- except Exception as e:
- logger.info('error e=<{}>'.format(e))
- return []
- @property
- def alipay_auth_app(self):
- if not self.customizedAlipayCashflowAllowable:
- primary_agent = self.primary_agent
- if self.is_equal(primary_agent):
- return self.inhouse_prime_agent.alipay_auth_app
- else:
- return primary_agent.alipay_auth_app
- app = self.my_ali_pay_app # type: AliApp
- logger.debug("app id is:{}".format(str(app.id)))
- if not app.valid:
- raise Exception(u'系统配置错误(第三方支付)')
- app.occupantId = str(self.id)
- return app
- def income_by_date(self, date, specific_source = None):
- """
- :param date:
- :param specific_source:
- :return:
- """
- reports = AgentIncomeReport.objects(agentId = str(self.id), date = date)
- if specific_source:
- reports = reports.filter(source = specific_source)
- return RMB(reports.sum('amount'))
- def today_income(self, specific_source = None):
- today = datetime.datetime.now().strftime(Const.DATE_FMT)
- return self.income_by_date(date = today, specific_source = specific_source)
- def yesterday_income(self, specific_source = None):
- yesterday = (datetime.datetime.now() - datetime.timedelta(days = 1)).strftime(Const.DATE_FMT)
- return self.income_by_date(date = yesterday, specific_source = specific_source)
- def aggregate_income(self, specific_source = None):
- """
- 获取聚合收入
- :param specific_source: 特定source
- :return:
- """
- if not specific_source:
- return sum_rmb(self.aggregatedIncome.values())
- else:
- return sum_rmb([v for k, v in self.aggregatedIncome.items() if k == specific_source])
- def _single_month_income(self, year, month, specific_source):
- if not specific_source:
- reports = AgentIncomeReport.objects(agentId = str(self.id), date__startswith = '%d-%02d' % (year, month))
- else:
- reports = AgentIncomeReport.objects(agentId = str(self.id), date__startswith = '%d-%02d' % (year, month),
- source = specific_source)
- return RMB(reports.sum('amount'))
- def current_month_income(self, specific_source = None):
- now = datetime.datetime.now()
- return self._single_month_income(now.year, now.month, specific_source)
- def monthly_income(self, specific_source = None):
- """
- 获得月报表
- :param specific_source:
- :return:
- """
- now = datetime.datetime.now()
- years = {_ for _ in range(2017, now.year + 1)}
- months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
- return {(year, month): self._single_month_income(year, month, specific_source)
- for year, month in itertools.product(years, months)}
- def is_equal(self, agent):
- return str(self.id) == str(agent.id)
- @property
- def supported_device_types(self):
- result = DeviceType.all(str(self.id))
- if result:
- return result
- primary_agent = self.primary_agent
- if not self.is_equal(primary_agent):
- return primary_agent.supported_device_types
- else:
- return []
- @property
- def feature_boolean_map(self):
- # type: ()->Dict[str, bool]
- features = super(Agent, self).feature_boolean_map
- if self.customizedWechatCashflowAllowable:
- features.update({
- 'show_withdraw_management': True
- })
- return features
- @property
- def hide_consume_kinds_dealer(self):
- rv = []
- if 'hiddenUsedTime' in self.features:
- rv.append(DEALER_CONSUMPTION_AGG_KIND.DURATION)
- if 'hidden_coins' in self.features:
- rv.append(DEALER_CONSUMPTION_AGG_KIND.COIN)
- left_features = set(self.features) - {'hiddenUsedTime', 'hidden_coins'}
- for feature in left_features:
- if feature.startswith('hide_') and feature.endswith('_for_dealer'):
- kind = feature.replace('hide_', '').replace('_for_dealer', '')
- if kind in DEALER_CONSUMPTION_AGG_KIND.choices():
- rv.append(kind)
- return rv
- @property
- def hide_consume_kinds_user(self):
- rv = []
- if 'hiddenUsedTime' in self.features:
- rv.append(DEALER_CONSUMPTION_AGG_KIND.DURATION)
- if 'hidden_coins' in self.features:
- rv.append(DEALER_CONSUMPTION_AGG_KIND.COIN)
- left_features = set(self.features) - {'hiddenUsedTime', 'hidden_coins'}
- for feature in left_features:
- if feature.startswith('hide_') and feature.endswith('_for_user'):
- kind = feature.replace('hide_', '').replace('_for_user', '')
- if kind in DEALER_CONSUMPTION_AGG_KIND.choices():
- rv.append(kind)
- return rv
- @property
- def withdraw_sms_provider(self):
- return agentWithdrawSMSProvider
- @property
- def withdraw_sms_phone_number(self):
- return str(self.username)
- def incr_income(self, source, source_key, money):
- # type: (str, str, RMB)->bool
- assert isinstance(money, RMB), 'money has to be a RMB instance'
- assert source in AGENT_INCOME_SOURCE.choices(), 'not support this source'
- assert source_key, 'source key must not be none'
- income_type = AgentConst.MAP_SOURCE_TO_TYPE[source]
- query = {'_id': ObjectId(self.id)}
- update = {
- '$inc': {
- 'aggregatedIncome.{source}'.format(source=source): money.mongo_amount,
- 'incomeMap.{source}'.format(source=source): money.mongo_amount,
- '{filed}.{key}.balance'.format(filed=AgentConst.MAP_TYPE_TO_FIELD[income_type],
- key=source_key): money.mongo_amount
- },
- }
- result = self.get_collection().update_one(query, update, upsert=False) # type: UpdateResult
- return bool(result.modified_count == 1)
- def decr_income(self, source, source_key, money): # type:(str, str, RMB) -> bool
- """
- 扣除代理商的收益
- 必须由调用方保证不会调用重入
- """
- return self.incr_income(source, source_key, -money)
- def check_withdraw_min_fee(self, income_type, pay_type, amount):
- if pay_type == WITHDRAW_PAY_TYPE.BANK:
- minimum = RMB(settings.WITHDRAW_MINIMUM)
- else:
- if income_type == AGENT_INCOME_TYPE.AD:
- minimum = RMB(settings.AD_WITHDRAW_MINIMUM)
- elif income_type == AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE:
- minimum = RMB(settings.SIM_INCOME_WITHDRAW_MINIMUM)
- else:
- minimum = RMB(settings.WITHDRAW_MINIMUM)
- if amount < minimum:
- raise ServiceException(
- {'result': 0, 'description': u"提现实际到账金额不能少于%s元" % (minimum,), 'payload': {}})
- def new_withdraw_record(self, withdraw_gateway, pay_entity, source_key, income_type, amount, pay_type, manual,
- recurrent):
- # type: (WithdrawGateway, WithdrawBankCard, str, str, RMB, str, bool, bool) -> WithdrawRecord
- if income_type in [AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE, AGENT_INCOME_TYPE.AD]:
- withdraw_fee_ratio = Permillage('0.00') # type: Permillage
- else:
- withdraw_fee_ratio = Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO # type: Permillage
- service_fee = amount * withdraw_fee_ratio.as_ratio # type: RMB
- withdraw_agent = withdraw_gateway.occupant # type: Agent
- has_bank_fee = withdraw_agent.dealerBankWithdrawFee and \
- self.bankWithdrawFee and \
- pay_type == WITHDRAW_PAY_TYPE.BANK
- if has_bank_fee:
- bank_trans_fee = min(RMB('25.00'), max(RMB('0.10'), amount * Permillage('1').as_ratio))
- else:
- bank_trans_fee = RMB('0.00')
- actual_pay = amount - service_fee - bank_trans_fee # type: RMB
- self.check_withdraw_min_fee(income_type, pay_type, actual_pay)
- return WithdrawRecord.create(self,
- withdraw_gateway = withdraw_gateway,
- pay_entity = pay_entity,
- source_key = source_key,
- income_type = income_type,
- pay_type = pay_type,
- fund_map = {
- 'amount': amount,
- 'serviceFee': service_fee,
- 'actualPay': actual_pay,
- 'bankTransFee': bank_trans_fee,
- 'withdrawFeeRatio': withdraw_fee_ratio,
- 'partition': []
- },
- manual = manual,
- recurrent = recurrent)
- def new_withdraw_handler(self, record):
- # type: (WithdrawRecord) -> WithdrawHandler
- from apps.web.agent.withdraw import AgentWithdrawHandler
- return AgentWithdrawHandler(self, record)
- def get_bound_pay_openid(self, key):
- # type: (str)->str
- pay_openid_map = self.payOpenIdMap # type: dict
- bound = pay_openid_map.get(key, BoundOpenInfo(openId = '')) # type: BoundOpenInfo
- return str(bound.openId)
- def set_bound_pay_openid(self, key, **payload):
- # type: (str, Dict)->None
- self.payOpenIdMap[key] = BoundOpenInfo(**payload)
- def get_online_moni_app(self):
- apps = MoniApp.objects.filter(appId__in = self.moniAppList, status=MoniAppStatus.ADDING).order_by('-priority')
- if apps.count() == 0:
- return {}
- app = apps.first() # type: MoniApp
- return {
- 'appId': app.appId,
- 'secret': app.secret,
- 'title': app.title,
- 'appName': app.appName,
- 'desc': app.desc
- }
- @property
- def agentIds(self):
- # 自身 有agentId 配置的有公众号的,返回自身的id, 没有配置公众号的, 找到厂商的祝代理商的id 以及自身的id返回
- if hasattr(self, "wechatUserManagerialApp") and self.wechatUserManagerialApp:
- return [str(self.id)]
- manager = Manager.objects.get(id = self.managerId)
- agent = Agent.objects.get(id = str(manager.primeAgentId))
- return [str(agent.id), str(self.id)]
- def is_my_product_user(self, user):
- return str(self.id) == user.productAgentId
- @property
- def my_avatar(self):
- if self.avatar:
- return self.avatar
- logo = self.product_logo
- if not logo:
- logo = ''
- return logo
- @property
- def abnormal(self):
- return self.status == self.Status.ABNORMAL
- @property
- def deviceIncomeShow(self):
- """
- 兼容之前数据库字段的方式
- """
- if "deviceIncomeShow" in self.features:
- return True
- return getattr(self, '_deviceIncomeShow', False)
- @deviceIncomeShow.setter
- def deviceIncomeShow(self, value):
- """
- 兼容之前的数据库 防止报错
- """
- setattr(self, '_deviceIncomeShow', value)
- class AgentIncomeReport(Searchable):
- agentId = StringField(verbose_name = '代理商ID')
- detail = StrictDictField(verbose_name = '详情', default = {})
- source = StringField(verbose_name = '收入来源')
- amount = MonetaryField(verbose_name = '数额', default = RMB(0))
- mchid = StringField(verbose_name = '资金账号')
- date = StringField(verbose_name = '日期', default = lambda: datetime.datetime.now().strftime(Const.DATE_FMT))
- dateTimeAdded = DateTimeField(verbose_name = '生成时间', default = datetime.datetime.now)
- meta = {"collection": "agent_income_reports", "db_alias": "report"}
- def __repr__(self):
- return '<AgentIncomeReport agentId=%s, source=%s, date=%s>' % (self.agentId, self.source, self.date)
- @property
- def title(self):
- if self.source == AGENT_INCOME_SOURCE.DEALER_WITHDRAW_FEE:
- return u'提现收益-经销商({}) 提现金额({})'.format(str(self.detail.get('name', '')), str(self.detail.get('withdrawAmount', '')))
- elif self.source == AGENT_INCOME_SOURCE.DEALER_CARD_FEE:
- return u'流量卡收益-经销商(%s) 充值(%s)' % (self.detail.get('name', ''), self.detail.get('sum_of_price', ''))
- elif self.source == AGENT_INCOME_SOURCE.AD:
- return u'广告收益-广告(%s) 设备(%s) 经销商(%s)' \
- % (self.detail.get('adId', ''), self.detail.get('logicalCode', ''), self.detail.get('dealer', ''))
- elif self.source == AGENT_INCOME_SOURCE.DEALER_DEVICE_FEE:
- return u'设备收益-经销商(%s) 设备(%s) 地址(%s)' \
- % (self.detail.get('name', ''), self.detail.get('logicalCode', ''), self.detail.get('groupName', ''))
- elif self.source == AGENT_INCOME_SOURCE.INSURANCE:
- return u'保险收益-经销商({}) 地址({})'.format(self.detail.get('name', ''), self.detail.get('groupName', ''))
- @classmethod
- def get_income_list(cls, **filters):
- if 'endDate' in filters:
- filters['date__lt'] = filters.pop('endDate')
- return cls.objects(**filters).order_by('-createdTime')
- class MoniApp(Searchable):
- # 公众号的原始属性 其中appToken用于微信验证服务器 由我们自行设置
- appName = StringField(verbose_name=u"公众号的名称", default="")
- appid = StringField(verbose_name=u"appId", unique=True)
- rawAppId = StringField(verbose_name=u"公众号的微信号", unique=True)
- secret = StringField(verbose_name=u"秘钥", default="")
- appToken = StringField(verbose_name="token", default="")
- appType = StringField(verbose_name="公众号类型", default="wechat")
- agentId = StringField(verbose_name=u"公众号所属的Agent")
- priority = IntField(verbose_name="加粉的数量", default=0)
- status = IntField(verbose_name="当前的状态", default=MoniAppStatus.ADDING, choices=MoniAppStatus.choices())
- desc = StringField(verbose_name='展示描述', default = '')
- title = StringField(verbose_name=u'展示加粉的说明title', default="")
- # TODO 需要将agentId 以及status 设置为联合索引确保唯一性
- meta = {"collection": "moni_app", "db_alias": "default"}
- def __str__(self):
- return "<{}>-<{}>".format(self.appName, self.rawAppId)
- @property
- def appId(self):
- return self.appid
- @property
- def occupantId(self):
- return self.agentId
- @classmethod
- def get_app_by_raw(cls, rawAppId):
- try:
- app = cls.objects.get(rawAppId=rawAppId)
- except DoesNotExist:
- return None
- return app
- @classmethod
- def get_app_by_agent(cls, agentId):
- """
- 获取代理商设置的 moniApp
- 找到加粉符合条件的最少的一个
- """
- agentId = str(agentId)
- return cls.objects.filter(agentId=agentId, status=MoniAppStatus.ADDING).first()
- @classmethod
- def get_inhouse_app(cls):
- return cls.get_app_by_agent(agentId=settings.MY_PRIMARY_AGENT_ID)
- def to_dict(self):
- return {
- 'appid': self.appid,
- 'secret': self.secret,
- 'title': self.title,
- 'appName': self.appName,
- 'desc': self.desc
- }
- @classmethod
- def subscribe(cls, app):
- cls.objects.filter(appid=str(app.appid)).update(inc__priority=1)
- @classmethod
- def unSubscribe(cls, app):
- cls.objects.filter(appid=str(app.appid)).update(dec__priority=1)
- class MoniAppPoint(Searchable):
- key = StringField(verbose_name = 'key', default = '')
- name = StringField(verbose_name = 'name', default = '')
- desc = StringField(verbose_name = 'desc', default = '')
- meta = {"collection": "moni_app_point", "db_alias": "default"}
|