12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- """
- web.dealer.models
- ~~~~~~~~~
- """
- import datetime
- import logging
- import uuid
- from operator import itemgetter
- from bson.objectid import ObjectId
- from django.conf import settings
- from django.core.cache import cache
- from mongoengine import EmbeddedDocument, StringField, BooleanField, EmbeddedDocumentField, MapField, DynamicDocument, \
- DateTimeField, Document, IntField, ListField, DictField, FloatField, ObjectIdField, EmbeddedDocumentListField, \
- LazyReferenceField, DynamicEmbeddedDocument
- from typing import Dict, AnyStr, Any, List, Optional, TYPE_CHECKING
- from apilib.monetary import RMB, Percent, sum_rmb, Permillage
- from apilib.systypes import IterConstant
- from apilib.utils_string import cn
- from apilib.utils_url import add_query
- from apps.web.agent.models import Agent, MoniAppPoint
- from apps.web.common.models import UserSearchable, MonthlyPackageTemp
- from apps.web.common.models import WithdrawRecord, CapitalUser, Balance
- from apps.web.common.transaction import OrderNoMaker, OrderMainType, DealerPaySubType, WITHDRAW_PAY_TYPE, RefundSubType
- from apps.web.constant import TYPE_ADJUST_USER_VIRTUAL_CARD, DEALER_CONSUMPTION_AGG_KIND, \
- DEALER_CONSUMPTION_AGG_KIND_TRANSLATION, Const, APP_TYPE
- from apps.web.core import PayAppType, ROLE, APP_KEY_DELIMITER
- from apps.web.core.db import Searchable, MonetaryField, PercentField, PermillageField
- from apps.web.core.exceptions import UpdateError, ServiceException, InvalidParameter
- from apps.web.core.messages.sms import dealerWithdrawSMSProvider, dealerMonitorWithdrawSMSProvider
- from apps.web.core.models import BoundOpenInfo
- from apps.web.core.payment import WithdrawGateway
- from apps.web.dealer.constant import TodoDone, TodoTypeEnum, LinkageSwitchEnum
- from apps.web.dealer.define import DEALER_INCOME_SOURCE, DEALER_INCOME_TYPE, DealerConst, \
- DEALER_INCOME_SOURCE_TRANSLATION
- from apps.web.device.models import Device, Group, GroupDict, DeviceType
- from apps.web.helpers import get_app, get_user_manager_agent
- from apps.web.utils import concat_dealer_access_entry_url, concat_front_end_url, concat_server_end_url
- if TYPE_CHECKING:
- from apps.web.common.transaction import WithdrawHandler
- from apps.web.core.payment import PaymentGateway
- from apps.web.core import PayAppBase
- logger = logging.getLogger(__name__)
- class DealerCacheMgr():
- pass
- class DealerAddr(EmbeddedDocument):
- id = StringField(verbose_name = 'id', default = str(uuid.uuid4()))
- default = BooleanField(verbose_name = u'是否默认地址', default = False)
- name = StringField(verbose_name = 'name', default = '')
- tel = StringField(verbose_name = 'telno', default = '')
- addr = StringField(verbose_name = 'addr', default = '')
- class ApiAppInfo(DynamicDocument):
- people = StringField(verbose_name=u"联系人", default='')
- tel = StringField(verbose_name=u"联系电话", default='')
- callbackUrl = StringField(verbose_name=u"回调地址Url", default='')
- apiDeviceMax = IntField(verbose_name=u'API最大接入数量', default=0)
- apiDevicePerCost = MonetaryField(verbose_name=u'API配额单价', default=RMB(50))
- meta = {
- 'collection': 'api_app_info',
- 'db_alias': 'default'
- }
- class DisableAdPlan(DynamicDocument):
- cycle = IntField(verbose_name=u'单次购买周期(单位:月)', default=12)
- disableAdCost = MonetaryField(verbose_name=u'纯净版单价', default=RMB(20))
- meta = {
- 'collection': 'disable_ad_plan',
- 'db_alias': 'default'
- }
- class ConfigTemplate(EmbeddedDocument):
- rechargeDiscount = ListField(verbose_name=u'优惠充值模板', default=[])
- cardRechargeDiscount = ListField(verbose_name=u'卡优惠充值模板', default=[])
- class DealerLinkageSwitch(DynamicEmbeddedDocument):
- """
- 经销商相关开关项
- """
- chargeInsurance = BooleanField(default=False, verbose_name=u"充电险开关")
- agentProxyServicePhone = BooleanField(verbose_name=u'代理商是否接管客服', default=False)
- def to_dict(self):
- return {"chargeInsurance": self.chargeInsurance, 'agentProxyServicePhone': self.agentProxyServicePhone}
- class Dealer(CapitalUser):
- """
- 经销商
- """
- INCOME_SOURCE_LIST = DEALER_INCOME_SOURCE.choices()
- INCOME_SOURCE_TO_TYPE = DealerConst.MAP_SOURCE_TO_TYPE
- INCOME_TYPE_LIST = DEALER_INCOME_TYPE.choices()
- INCOME_TYPE_TO_FIELD = DealerConst.MAP_TYPE_TO_FIELD
- #: 默认管理平台微信公众号授权用户信息
- DEFAULT_WECHAT_AUTH_USER_INFO = {
- 'avatar': '',
- 'sex': 0
- }
- DEFAULT_CHECKPOINT = {
- 'gerenzhongxin': False,
- 'yue': False,
- 'baogaolaoban': False,
- 'fukuan': False
- }
- CACHE_KEY = 'dealer-info-{id}'
- CacheMgr = DealerCacheMgr
- wechat = StringField(verbose_name = "微信", default = "")
- description = StringField(verbose_name = "描述", default = "")
- phone = StringField(verbose_name = "电话", default = "")
- # 相当于 经销商的资金池
- deviceBalance = MapField(EmbeddedDocumentField(document_type=Balance))
- # 经销商获取的分账收益 在消费订单结束的时候进行记录
- ledgerBalance = MapField(EmbeddedDocumentField(document_type=Balance))
- adBalance = MapField(EmbeddedDocumentField(document_type=Balance))
- payOpenIdMap = MapField(EmbeddedDocumentField(BoundOpenInfo))
- isRead = StringField(verbose_name = "是否已经读了消息", default = "N")
- noPassReason = StringField(verbose_name = "未通过原因", default = "")
- cibMerchant = BooleanField(verbose_name = "是否立即生效", default = False)
- # 推送消息
- managerialAppId = StringField(verbose_name = "auth,notify app id", default = "")
- managerialOpenId = StringField(verbose_name = "后台,接受推送消息", default = "")
- wechatAuthUserInfo = DictField(verbose_name = "经销商授权后获取的用户信息", default = DEFAULT_WECHAT_AUTH_USER_INFO)
- managerialWechatBoundTime = DateTimeField(verbose_name = "绑定微信的时间")
- agentId = StringField(verbose_name = "从属于哪个代理商", null = False)
- # 上级代理商设置的经营分成比例策略
- agentProfitShare = PercentField(verbose_name='代理商设备运营分成比例', default = Percent('0.00'))
- agentMerProfitShare = PercentField(verbose_name='代理商设备运营分成比例 商户收款', default = Percent('0.00'))
- #: 默认选项|开关
- #: 选项
- adShow = BooleanField(verbose_name = "广告显示与否的选项", default = True)
- noAdPolicy = StringField(verbose_name = "不打开广告情况下的显示策略(notshow,noshow,banner)", default = 'noshow')
- beforeCharge = BooleanField(verbose_name = "使用前必须充值开关", default = False)
- noRecharge = BooleanField(verbose_name = "不需要充值功能", default = False)
- defaultWashConfig = DictField(verbose_name = "与经销商绑定的washconfig", default = {})
- #: 通知选项
- withdrawlNotify = BooleanField(verbose_name = "提现通知", default = False)
- offlineNotifySwitch = BooleanField(verbose_name = "设备离线通知开关", default = False)
- offlineNotifyTime = StringField(verbose_name = '设备离线通知时间', default = '')
- dailyIncomeReportPushSwitch = BooleanField(verbose_name = '是否开启次日9点推送昨日收益报表', default = False)
- newUserPaymentOrderPushSwitch = BooleanField(verbose_name = '是否开启', default = False)
- devFaultPushDealerSwitch = BooleanField(verbose_name = '设备故障通知开关,是否推送给经销商', default = False)
- devFaultPushUserSwitch = BooleanField(verbose_name = '设备故障通知开关,是否推送给用户', default = False)
- #: 用于客服的联系方式
- serviceName = StringField(verbose_name = "客服名称", default = "", max_length = 32)
- servicePhone = StringField(verbose_name = "客服电话", default = "", max_length = 32)
- qrcodeUrl = StringField(verbose_name = "二维码图片链接", default = "", max_length = 256)
- # 提现费率. 由代理商设置
- 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
- )
- # 流量卡年费
- annualTrafficCost = MonetaryField(default = Const.PLATFORM_DEFAULT_TRAFFIC_COST, verbose_name = '流量卡年费')
- # 该值由平台调整. 可以单独调整某些经销商的成本价格和卡年费
- trafficCardCost = MonetaryField(verbose_name = u'平台提供给经销商的流量卡成本价', default = None)
- limitAdLocation = BooleanField(default = False, verbose_name = u'是否限制广告领取的地域')
- features = ListField(verbose_name = u'支持的特性', default = [])
- defaultDiscountConfig = DictField(verbose_name = u"经销商默认优惠充值套餐", default = Const.DEFAULT_DISCOUNT_RULE)
- defaultCardDiscountConfig = DictField(verbose_name = u"经销商默认卡优惠充值套餐", default = Const.DEFAULT_DISCOUNT_RULE)
- supportedIncomeAggregate = ListField(verbose_name = u'支持的收入聚合', default = DEALER_INCOME_SOURCE.choices())
- supportedConsumptionAggregate = ListField(verbose_name=u'支持的消费聚合', default=DEALER_CONSUMPTION_AGG_KIND.choices())
- supportedConsumptionShow = ListField(verbose_name=u"用户端的消费信息显示", default=[])
- bottomAd = DictField(verbose_name = u"底部banner", default = {})
- pushBrokerUrl = StringField(verbose_name = u'经销商订阅的消息推送URL', default = settings.DEFAULT_DEALER_PUSH_BROKER_URL)
- limitDevNum = IntField(verbose_name = u'限制经销商最大设备数量', default = 9999)
- isPurePartner = BooleanField(verbose_name = u'是否为纯合伙人', default = False)
- userCount = IntField(verbose_name = u'总的用户数量', default = 0)
- moniAppCheckPointDict = DictField(verbose_name = u'是否需要弹出监督公众号的经销商检查点', default = DEFAULT_CHECKPOINT)
- forceFollowGzh = StringField(verbose_name=u'是否强制关注公众号', default='agent')#以代理商为准:'agent',强制关注:'yes',不允许强制关注:'no',‘永不关注’:‘never’,‘关不关注无所谓’:free
- devCount = IntField(verbose_name = u'设备数量,方便统计查询', default = 0)
- maxPackagePrice = MonetaryField(verbose_name = u"最大套餐价格", default = RMB(100.00))
- expressAddrList = EmbeddedDocumentListField(verbose_name = u'经销商的收货地址', document_type = DealerAddr)
- showServicePhone = BooleanField(verbose_name = u'是否允许使用服务电话', default = False)
- isShowBanner = BooleanField(verbose_name = u'是否显示代理商banner', default = True)
- hasTempPackage = BooleanField(verbose_name = u'是否启用临时套餐', default = False)
- freeModeDisplayedIdle = BooleanField(verbose_name = u'免费地址下端口状态显示为空闲', default = False)
- domain = StringField(verbose_name = u'api推送地址url', default = '')
- monitorPhone = StringField(verbose_name = u"提现审批员的手机号", default = "", max_length = 32)
- currencyCoins = BooleanField(verbose_name = u'金币是否通用(deprecated)', default = True)
- currencyMode = StringField(verbose_name = u'金币通用模式(allYes, allNo, asGroup)', default = None)
- # 有种情况下比如同一个手机号在两个代理商下申请的都有经销商 商户其实只用申请一个就行了
- bindMerSrc = StringField(verbose_name=u"共用的商户经销商ID")
- # 联动开关
- linkageSwitch = EmbeddedDocumentField(verbose_name=u"开关集合", document_type=DealerLinkageSwitch, default=DealerLinkageSwitch)
- apiInfo = LazyReferenceField(verbose_name=u'api应用信息', document_type=ApiAppInfo, default=None)
- disableAdPlan = LazyReferenceField(verbose_name=u'纯净版信息', document_type=DisableAdPlan, default=None)
- # 配置模板集合
- templateSet = EmbeddedDocumentField(verbose_name=u"开关集合", document_type=ConfigTemplate, default=ConfigTemplate)
- meta = {
- 'indexes': [
- {
- 'fields': ['username', 'agentId'], 'unique': True
- },
- ],
- "collection": "Dealer",
- "db_alias": "default"
- }
- search_fields = ('username', 'nickname', 'remarks')
- def __str__(self):
- return '{}<id={} username={} nickname={} agentId={}>'.format(
- self.__class__.__name__, str(self.id), self.username, self.nickname, self.agentId)
- @property
- def currency_mode(self):
- if not self.currencyMode:
- if self.currencyCoins:
- return 'allYes'
- else:
- return 'allNo'
- else:
- return self.currencyMode
- @property
- def bossId(self):
- return self.id
- @property
- def myBoss(self):
- return self
- # 覆写. 需要对ruleDict参数进行一次转换
- def save(self, force_insert = False, validate = True, clean = True,
- write_concern = None, cascade = None, cascade_kwargs = None,
- _refs = None, save_condition = None, **kwargs):
- if 'defaultDiscountConfig' in kwargs:
- newRuleDict = {}
- for k, v in self.format_default_discount.items():
- newRuleDict[k.replace('.', '-')] = v
- self.defaultDiscountConfig = newRuleDict
- if "defaultCardDiscountConfig" in kwargs:
- newCardRuleDict = {}
- for k, v in self.format_card_discount.items():
- newCardRuleDict[k.replace('.', '-')] = v
- self.defaultCardDiscountConfig = newCardRuleDict
- return super(Dealer, self).save(**kwargs)
- def update(self, **kwargs):
- try:
- ruleDict = kwargs.pop('defaultDiscountConfig', None)
- if ruleDict is not None:
- newRuleDict = {}
- for k, v in ruleDict.items():
- newRuleDict[k.replace('.', '-')] = v
- kwargs.update({'defaultDiscountConfig': newRuleDict})
- cardRuleDict = kwargs.pop('defaultCardDiscountConfig', None)
- if cardRuleDict is not None:
- newCardRuleDict = {}
- for k, v in cardRuleDict.items():
- newCardRuleDict[k.replace('.', '-')] = v
- kwargs.update({'defaultCardDiscountConfig': newCardRuleDict})
- updated = super(Dealer, self).update(**kwargs)
- if not updated:
- raise UpdateError('failed to update dealer query kwargs(%s)' % (kwargs,))
- else:
- return updated
- finally:
- self.invalid_cache(str(self.id))
- @property
- def permissionList(self):
- return []
- @property
- def virtualCardOnlyCardSwitch(self):
- """
- 虚拟卡仅用于 实体卡的开关
- TODO zjl 这个是临时加的一个开关,目的在于不修改前台 达到 虚拟卡只用于实体卡而不能微信扫码使用的目的
- 等虚拟卡这一块儿整改的时候直接将这个去掉
- :return:
- """
- return "virtualCardOnlyCardSwitch" in self.features
- @property
- def switches(self):
- return {
- 'adShow': self.adShow,
- 'beforeCharge': self.beforeCharge,
- 'noRecharge': self.noRecharge,
- 'withdrawlNotify': self.withdrawlNotify,
- 'offlineNotify': self.offlineNotifySwitch,
- 'dailyIncomeReportPushSwitch': self.dailyIncomeReportPushSwitch,
- 'newUserPaymentOrderPushSwitch': self.newUserPaymentOrderPushSwitch,
- 'devFaultPushDealerSwitch': self.devFaultPushDealerSwitch,
- 'devFaultPushUserSwitch': self.devFaultPushUserSwitch,
- 'virtualCardOnlyCardSwitch': self.virtualCardOnlyCardSwitch
- }
- def query_feature_by_list(self, queryList):
- # type:(list)->dict
- dealerFeatures = self.feature_boolean_map
- resultFeatures = {}
- agent = Agent.objects.get(id=self.agentId) # type: Agent
- agent_features = agent.feature_boolean_map
- for query in queryList:
- if query in dealerFeatures:
- resultFeatures[query] = dealerFeatures[query]
- else:
- if query in agent_features:
- resultFeatures[query] = agent_features[query]
- else:
- resultFeatures[query] = False
- return resultFeatures
- def get_feature(self, feature_name):
- return self.query_feature_by_list([feature_name])
- def to_dict(self, shadow = False):
- rv = super(Dealer, self).to_dict(shadow = shadow)
- pointList = []
- for key, switch in self.moniAppCheckPointDict.items():
- try:
- point = MoniAppPoint.objects.get(key = key)
- except Exception as e:
- continue
- pointList.append({'name': point.name, 'key': key, 'switch': switch})
- rv.update({
- 'id': str(self.id),
- 'deviceBalance': self.sub_balance(DEALER_INCOME_TYPE.DEVICE_INCOME),
- 'adBalance': self.sub_balance(DEALER_INCOME_TYPE.AD_INCOME),
- "ledgerBalance": self.sub_balance(DEALER_INCOME_TYPE.LEDGER_CONSUME),
- 'balance': self.total_balance,
- 'wechat': self.wechat,
- 'description': self.description,
- 'managerialOpenId': self.managerialOpenId,
- 'managerialAppId': self.managerialAppId,
- 'agentId': self.agentId,
- 'bottomAd': self.bottomAd,
- 'featureList': self.feature_list,
- 'withdrawFeeRatio': self.withdrawFeeRatio,
- 'annualTrafficCost': self.annualTrafficCost,
- 'defaultDiscountConfig': self.defaultDiscountConfig,
- 'pushBrokerUrl': self.pushBrokerUrl,
- 'isPurePartner': self.isPurePartner,
- 'pointDict': pointList,
- 'forceFollowGzh': self.forceFollowGzh,
- 'smsVendor': self.smsVendor,
- 'hasTempPackage': self.hasTempPackage,
- 'freeModeDisplayedIdle': self.freeModeDisplayedIdle,
- 'offlineNotifySwitch': self.offlineNotifySwitch,
- 'offlineNotifyTime': self.offlineNotifyTime,
- 'currencyMode': self.currency_mode,
- 'supportedConsumptionShow': self.supportedConsumptionShow,
- })
- if hasattr(self, 'displayTempPackage'):
- rv.update({"displayTempPackage": self.displayTempPackage})
- rv.update(self.switches)
- return rv
- @classmethod
- def all(cls):
- return [dealer.to_dict() for dealer in cls.objects.all()]
- @staticmethod
- def cache_key(ownerId):
- # type:(Optional[ObjectId, str])->str
- return Dealer.CACHE_KEY.format(id = str(ownerId))
- @staticmethod
- def get_dealer(ownerId): # type:(ObjectId)->Optional[DealerDict, None]
- if not ownerId:
- return None
- dealer = cache.get(Dealer.cache_key(ownerId)) # type: dict
- if dealer:
- return DealerDict(dealer)
- else:
- dealer = Dealer.objects(id = ownerId).first() # type: Dealer
- if dealer:
- value = dealer.to_dict()
- cache.set(Dealer.cache_key(ownerId), value)
- return DealerDict(value)
- else:
- logger.error('can not find dealer from db,id=%s' % ownerId)
- return None
- @staticmethod
- def invalid_cache(ownerId):
- # type:(Optional[ObjectId, str])->None
- cache.delete(Dealer.cache_key(ownerId))
- @staticmethod
- def update_dealer(ownerId, **valueDict):
- # type:(ObjectId, **Any)->bool
- try:
- if 'noRecharge' in valueDict and valueDict['noRecharge']:
- valueDict.update({'beforeCharge': False})
- elif 'beforeCharge' in valueDict and valueDict['beforeCharge']:
- valueDict.update({'noRecharge': False})
- Dealer.objects(id = ObjectId(ownerId)).update(**valueDict)
- except Exception as e:
- logger.exception('update dealer info error=%s' % e)
- return False
- Dealer.invalid_cache(ownerId)
- return True
- @property
- def isManagerialOpenIdBound(self):
- return self.managerialOpenId != ''
- 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 bound.openId
- def set_bound_pay_openid(self, key, **payload):
- # type: (str, Dict)->None
- self.payOpenIdMap[key] = BoundOpenInfo(**payload)
- def get_bound_pay_info(self, key):
- pay_openid_map = self.payOpenIdMap
- bound = pay_openid_map.get(key, BoundOpenInfo(openId = ''))
- return bound.to_detail_dict()
- def set_bound_pay_info(self, key, **payload):
- bound = BoundOpenInfo(**payload)
- self.payOpenIdMap[key] = bound
- return
- def isAutoWithdrawOpenIdBound(self, key):
- pay_openid_map = self.payOpenIdMap # type: dict
- bound = pay_openid_map.get(key, BoundOpenInfo(openId = ''))
- return bound.openId != "" and bound.nickname != ""
- @property
- def format_default_discount(self):
- tmp_rule_dict = {}
- for k, v in self.defaultDiscountConfig.items():
- tmp_rule_dict[k.replace('-', '.')] = v
- return tmp_rule_dict
- @property
- def format_card_discount(self):
- card_rule_dict = dict()
- for k, v in self.defaultCardDiscountConfig.items():
- card_rule_dict[k.replace("-", ".")] = v
- return card_rule_dict
- @staticmethod
- def get_dealers(agentId):
- dealers = Dealer.get_collection().find({'agentId': agentId})
- return [str(dealer['_id']) for dealer in dealers]
- @staticmethod
- def get_dealer_from_groupId(groupId):
- group = Group.get_group(groupId)
- if group is not None:
- dealer = Dealer.objects(id = group['ownerId']).first()
- else:
- group = Group.objects(id = groupId).first()
- dealer = Dealer.objects(id = group.ownerId).first()
- return dealer
- def get_own_devices(self):
- groupIds = Group.get_group_ids_of_dealer(str(self.id))
- devNoList = Device.get_devNos_by_group(groupIds)
- return Device.get_dev_by_nos(devNoList).values()
- @property
- def income_aggregate_source(self):
- # type: ()->Dict[str, AnyStr]
- """
- e.g. {'ad': u'广告'}
- :return:
- """
- supports = [item for item in self.supportedIncomeAggregate if item != 'ad']
- g = itemgetter(*supports)
- return dict(zip(supports, g(DEALER_INCOME_SOURCE_TRANSLATION)))
- @property
- def consumption_aggregate_source(self):
- # type: ()->Dict[str, AnyStr]
- """
- e.g. {'elect': u'电量'}
- :return:
- """
- g = itemgetter(*self.supportedConsumptionAggregate)
- return dict(zip(self.supportedConsumptionAggregate, g(DEALER_CONSUMPTION_AGG_KIND_TRANSLATION)))
- def set_agent_profit_share(self, share):
- # type: (Percent)->int
- """
- :param share:
- :return:
- """
- updated = self.update(agentProfitShare = share.mongo_amount)
- return updated
- def set_agent_mer_profit_share(self, share):
- # type: (Percent)->int
- """
- :param share:
- :return:
- """
- updated = self.update(agentMerProfitShare = share.mongo_amount)
- return updated
- @classmethod
- def set_traffic_costs_multiply(cls, dealer_ids, cost):
- # type: (List[ObjectId], RMB)->int
- updated = cls.get_collection().update_many({'_id': {'$in': dealer_ids}},
- {'$set': {'annualTrafficCost': cost.mongo_amount}},
- upsert = False)
- return updated
- @property
- def login_url(self):
- # type:()->str
- return concat_dealer_access_entry_url(agentId = self.agentId)
- @property
- def withdraw_sms_provider(self):
- if self.monitorPhone:
- return dealerMonitorWithdrawSMSProvider
- else:
- return dealerWithdrawSMSProvider
- @property
- def withdraw_sms_phone_number(self):
- if self.monitorPhone:
- return str(self.monitorPhone)
- else:
- return str(self.username)
- @property
- def auto_withdraw_bound_open_id(self):
- from apps.web.helpers import get_wechat_auth_bridge
- auth_bridge = get_wechat_auth_bridge(source = self, app_type = APP_TYPE.WECHAT_WITHDRAW)
- return self.get_bound_pay_openid(auth_bridge.bound_openid_key)
- @property
- def auto_withdraw_bank_account(self):
- return self.withdrawOptions.get('bankAccount', None)
- def record_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 DEALER_INCOME_SOURCE.choices(), 'not support this source'
- assert source_key, 'source key must not be none'
- income_type = DealerConst.MAP_SOURCE_TO_TYPE[source]
- return self.incr_fund(income_type, source_key, money)
- def new_withdraw_handler(self, record):
- # type: (WithdrawRecord) -> WithdrawHandler
- from apps.web.dealer.withdraw import DealerWithdrawHandler
- return DealerWithdrawHandler(self, record)
- 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 == DEALER_INCOME_TYPE.AD_INCOME:
- minimum = RMB(settings.AD_WITHDRAW_MINIMUM)
- else:
- minimum = RMB(settings.WITHDRAW_MINIMUM)
- if amount < minimum:
- raise ServiceException(
- {'result': 0, 'description': u"提现实际到账金额不能少于%s元" % (settings.WITHDRAW_MINIMUM,), 'payload': {}})
- def new_withdraw_record(self, withdraw_gateway, pay_entity, source_key, income_type, amount, pay_type, manual, recurrent):
- """
- 创建自动提现的单子
- :param withdraw_gateway: 提现网关 WechatPaymentGateway
- :param pay_entity: 银行卡 WithdrawBankCard
- :param income_type: 收益类型 str
- :param amount: 提现金额(非实际到账) RMB
- :param pay_type: 提现的方式 str
- :param manual: 手动提现 Bool
- :param recurrent: 是否自动提现 Bool
- :return: WithdrawRecord
- """
- agent = Agent.objects(id = self.agentId).first() # type: Agent
- if not agent:
- logger.error('agent is None, agentId={}'.format(self.agentId))
- raise ServiceException(
- {
- 'result': 0,
- 'description': u'系统繁忙,请稍后再试',
- 'payload': {}
- })
- if income_type == DEALER_INCOME_TYPE.AD_INCOME:
- agentFeeRation = Permillage('0')
- managerFeeRatio = Permillage('0')
- dealerFeeRatio = Permillage('0')
- else:
- agentFeeRation = agent.withdrawFeeRatio
- managerFeeRatio = agent.withdrawFeeRatioCost
- dealerFeeRatio = self.withdrawFeeRatio
- # 微信或者支付宝平台服务费
- serviceFee = amount * (dealerFeeRatio.as_ratio)
- # 计算微信转银行卡手续费
- has_bank_fee = False
- if pay_type == WITHDRAW_PAY_TYPE.BANK:
- if recurrent:
- has_bank_fee = self.auto_withdraw_bank_fee
- else:
- withdraw_agent = withdraw_gateway.occupant # type: Agent
- has_bank_fee = withdraw_agent.dealerBankWithdrawFee and self.bankWithdrawFee
- 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')
- actualPay = amount - serviceFee - bank_trans_fee
- self.check_withdraw_min_fee(income_type, pay_type, actualPay)
- if dealerFeeRatio > managerFeeRatio:
- earned = amount * ((dealerFeeRatio - managerFeeRatio).as_ratio)
- if earned < RMB(0):
- earned = RMB(0)
- elif earned > serviceFee:
- earned = serviceFee
- # 分给代理商
- agentEarned = RMB(0)
- if dealerFeeRatio > agentFeeRation:
- agentEarned = amount * ((dealerFeeRatio - agentFeeRation).as_ratio)
- if agentEarned < RMB(0):
- agent_earned = RMB(0)
- elif agentEarned > earned:
- agentEarned = earned
- # 代理商分完如果还有 继续分给厂商
- managerEarned = (earned - agentEarned)
- if managerEarned < RMB(0):
- managerEarned = RMB(0)
- else:
- agentEarned = RMB(0)
- managerEarned = RMB(0)
- partition = [
- {
- 'role': ROLE.agent,
- 'id': self.agentId,
- 'cost': agentFeeRation.mongo_amount,
- 'earned': agentEarned.mongo_amount
- },
- {
- 'role': ROLE.manager,
- 'id': str(agent.primary_agent.id),
- 'cost': managerFeeRatio.mongo_amount,
- 'earned': managerEarned.mongo_amount
- }
- ]
- 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': serviceFee,
- 'bankTransFee': bank_trans_fee,
- 'actualPay': actualPay,
- 'withdrawFeeRatio': dealerFeeRatio,
- 'partition': partition
- },
- manual = manual,
- recurrent = recurrent
- )
- def get_addr_list(self):
- result = []
- for obj in self.expressAddrList:
- if not obj.default:
- result.append({'tel':obj.tel,'name':obj.name,'addr':obj.addr,'id':str(obj.id),'default':obj.default})
- else:
- result.insert(0, {'tel':obj.tel,'name':obj.name,'addr':obj.addr,'id':str(obj.id),'default':obj.default})
- return result
- @classmethod
- def get_auto_withdraw_dealers(cls):
- dealers = list()
- for _dealer in cls.objects.filter(withdrawOptions__autoWithdrawSwitch = True): # type: Dealer
- if "autoWithdraw" in _dealer.features:
- dealers.append(_dealer)
- return dealers
- @classmethod
- def get_income_balance_list(cls, dealer, incomeType, minNum=0):
- """
- 计算经销商在 每个收益来源下面的可提现余额 以及 手续费
- :param dealer:
- :param incomeType:
- :param minNum: 过滤金额
- :return: list --> (sourceKey, amount) 来源 当前账户余额
- """
- assert incomeType in DEALER_INCOME_TYPE.choices(), 'not support this income type'
- balanceDict = getattr(dealer, DealerConst.MAP_TYPE_TO_FIELD[incomeType])
- withdrawInfoList = list()
- for sourceKey, item in balanceDict.items():
- if not WithdrawGateway.is_ledger(sourceKey):
- continue
- balance = item.balance
- if balance <= RMB(0):
- continue
- if balance < RMB(minNum):
- continue
- withdrawInfoList.append((sourceKey, balance))
- return withdrawInfoList
- @classmethod
- def factory(cls, **kwargs):
- factory_type = kwargs.pop('factory_type')
- if factory_type == 'app':
- app_type = kwargs.pop('app_type')
- return lambda dealer: cls.from_dealer(dealer = dealer, app_type = app_type, **kwargs)
- else:
- raise InvalidParameter(u'参数错误')
- @classmethod
- def from_dealer(cls, dealer, app_type, **kwargs):
- appKey = "{}_app".format(app_type)
- attr_or_func = getattr(dealer, appKey)
- if callable(attr_or_func):
- return attr_or_func(**kwargs)
- else:
- return attr_or_func
- @property
- def my_avatar(self):
- if self.avatar:
- return self.avatar
- if self.isManagerialOpenIdBound and self.wechatAuthUserInfo:
- return self.wechatAuthUserInfo.get('avatar', '')
- return ''
- def withdraw_source_key(self, pay_app = None):
- # type:(PayAppBase)->str
- """
- 经销商提现资金池KEY是虚拟的, 仅仅对应账号的资金统计值
- :param pay_app:
- :return:
- """
- return APP_KEY_DELIMITER.join(
- [WithdrawGateway.NO_LEDGER_PREFIX, pay_app.pay_app_type, getattr(pay_app, '__gateway_key__')])
- @classmethod
- def get_cooperative_dealer_ids(cls, dealerId):
- rcds = Group.get_collection().find({'partnerList.id': dealerId})
- dealer_ids = set([dealerId])
- for rcd in rcds: # type: Group
- dealer_ids.add(rcd['ownerId'])
- return list(dealer_ids)
- @classmethod
- def get_cooperative_group_ids(cls, dealerId):
- gs = Group.get_collection().find({'partnerList.id': dealerId})
- groupIds = []
- for _ in gs:
- groupIds.append(str(_['_id']))
- gsMe = Group.get_collection().find({'ownerId': dealerId})
- for _ in gsMe:
- groupIds.append(str(_['_id']))
- return groupIds
- @property
- def is_inhouse_wallet(self):
- app = get_app(source = self, app_type = APP_TYPE.WECHAT_ENV_PAY, role = ROLE.myuser)
- return getattr(app, 'inhouse', False)
- @property
- def defaultMonthlyPackage(self):
- """
- 默认的包月套餐的列表
- :return:
- """
- return MonthlyPackageTemp.default_one(ownerId=str(self.id))
- @property
- def monthlyPackage(self):
- """
- 所有的包月套餐的列表
- :return:
- """
- return MonthlyPackageTemp.get_template_by_dealer(str(self.id))
- def isJosEnable(self, clientEnv="wechat"):
- """
- 判断该经销商是否能够使用京东的拉新活动
- # 由于分账的原因 目前只能够走 使用我们自身资金池的用户
- :param clientEnv: 客户端的环境
- :return:
- """
- return False
- def limit_filter_date(self, startTime, endTime):
- # type:(str, str)->(str, str)
- if not startTime:
- startTime = datetime.datetime.now().strftime('%Y-%m-%d')
- if not endTime:
- endTime = datetime.datetime.now().strftime('%Y-%m-%d')
- if str(self.id) == '5d132407003048494763a51b':
- # 港德的隐藏数据需求
- if startTime < '2021-01-01':
- startTime = '2021-01-01'
- if endTime < '2021-01-01':
- startTime = '2021-01-01'
- endTime = '2020-12-31'
- else:
- if startTime > endTime:
- endTime = startTime
- return startTime, endTime
- @classmethod
- def limit_filter_year(cls, dealerId, year):
- if dealerId == '5d132407003048494763a51b':
- # 港德的隐藏数据需求
- if int(year) < 2021:
- return None
- return year
- def get_currency_group_ids(self, group, groups = None):
- # type: (Optional[Group, GroupDict], List[Optional[Group, GroupDict]])->list
- dealer_dict = DealerDict(self.to_dict()) # type: DealerDict
- return dealer_dict.get_currency_group_ids(group, groups)
- @property
- def ad_show(self):
- if self.adShow:
- return 'show'
- else:
- return self.noAdPolicy
- @property
- def minProfitShare(self):
- """
- 经销商 所有的地址中 经销商的最低的分成比例
- :return:
- """
- profitShare = Percent('100')
- for _group in Group.objects.filter(ownerId=str(self.id)):
- if not _group.partnerList:
- continue
- for _partner in _group.partnerList:
- _profitShare = Percent(_partner["percent"])
- if _profitShare < profitShare:
- profitShare = _profitShare
- return profitShare
- def rent_freeze_balance(self, income_type, money, source_key, transaction_id): # type:(str, RMB, str, str)->bool
- """
- 出租设备的冻结订单
- :param income_type:
- :param money:
- :param source_key:
- :param transaction_id:
- :return:
- """
- assert source_key, 'gateway is null'
- assert transaction_id, 'transaction id is null'
- orderType = "inHandRentOrderList"
- field = self.income_field_name(income_type = income_type)
- fund_key = self.fund_key(income_type = income_type, source_key = source_key)
- query = {'_id': self.id, '{}.transaction_id'.format(orderType): {'$ne': transaction_id}}
- update = {
- '$inc': {
- '{fund_key}.balance'.format(fund_key = fund_key): (-money).mongo_amount
- },
- '$addToSet': {
- '{}'.format(orderType): {
- 'transaction_id': transaction_id,
- 'field': field,
- 'key': source_key,
- 'value': money.mongo_amount
- }
- }
- }
- result = self.get_collection().update_one(query, update, upsert = False)
- return bool(result.modified_count == 1)
- def rent_clear_frozen_balance(self, transaction_id): # type:(str)->bool
- """
- 订单扣款完毕 清除冻结信息
- :param transaction_id:
- :return:
- """
- assert transaction_id, 'transaction id is null'
- orderType = "inHandRentOrderList"
- query = {'_id': self.id, '{}.transaction_id'.format(orderType): transaction_id}
- update = {
- '$pull': {
- '{}'.format(orderType):
- {
- 'transaction_id': transaction_id
- }
- }
- }
- result = self.get_collection().update_one(query, update, upsert = False)
- return bool(result.modified_count == 1)
- def rent_recover_frozen_balance(self, income_type, money, source_key, transaction_id): # type:(str, RMB, str, str)->bool
- """
- 回滚冻结的支付订单余额
- :param source_key:
- :param income_type:
- :param money:
- :param transaction_id:
- :return:
- """
- assert source_key, 'gateway is null'
- assert transaction_id, 'transaction id is null'
- orderType = "inHandRentOrderList"
- fund_key = self.fund_key(income_type = income_type, source_key = source_key)
- query = {'_id': self.id, '{}.transaction_id'.format(orderType): transaction_id}
- update = {
- '$inc': {
- '{fund_key}.balance'.format(fund_key = fund_key): money.mongo_amount
- },
- '$pull': {
- '{}'.format(orderType):
- {
- 'transaction_id': transaction_id
- }
- }
- }
- result = self.get_collection().update_one(query, update, upsert = False)
- return bool(result.modified_count == 1)
- def get_linkage_switch(self):
- """
- 获取经销商的两级联动开关设置
- """
- return {
- LinkageSwitchEnum.CHARGE_INSURANCE: self.linkageSwitch.chargeInsurance
- }
- def turn_on(self, category):
- """
- 打开保险的开关
- """
- # 打开保险前的前置检查
- if category == LinkageSwitchEnum.CHARGE_INSURANCE:
- if not self.is_inhouse_wallet:
- return False
- self.linkageSwitch.chargeInsurance = True
- return bool(self.update(linkageSwitch__chargeInsurance=self.linkageSwitch.chargeInsurance))
- def turn_off(self, category):
- """
- 关掉保险的开关
- """
- if category == LinkageSwitchEnum.CHARGE_INSURANCE:
- self.linkageSwitch.chargeInsurance = False
- return bool(self.update(linkageSwitch__chargeInsurance=self.linkageSwitch.chargeInsurance))
- def get_all_api_devs(self):
- devList = Device.get_devs_by_ownerId(str(self.id))
- return filter(lambda _: _.isApi == True, devList)
- @property
- def api_quota_info(self):
- totalQuota = self.api_app.apiDeviceMax
- devs = self.get_all_api_devs()
- usedQuota = len(devs)
- remainingQuota = totalQuota - usedQuota if totalQuota - usedQuota > 0 else 0
- return {
- 'totalQuota': totalQuota,
- 'usedQuota': usedQuota,
- 'remainingQuota': remainingQuota
- }
- @property
- def api_app(self):
- if self.apiInfo:
- return self.apiInfo.fetch()
- else:
- return ApiAppInfo()
- def update_api_app(self, **payload):
- if not self.apiInfo:
- self.apiInfo = ApiAppInfo().save()
- self.save()
- apiInfo = self.apiInfo.fetch() # type: ApiAppInfo
- apiInfo.update(**payload)
- @property
- def disable_ad_plan(self):
- if self.disableAdPlan: # type: DisableAdPlan
- return self.disableAdPlan.fetch()
- else:
- return DisableAdPlan()
- def update_disable_ad_plan(self, **payload):
- if not self.disableAdPlan:
- self.disableAdPlan = DisableAdPlan().save()
- self.save()
- disable_ad_plan = self.disableAdPlan.fetch() # type: DisableAdPlan
- disable_ad_plan.update(**payload)
- def disable_ad_quota_info(self):
- all_devs = Device.get_devs_by_ownerId(str(self.id))
- now = datetime.datetime.now()
- configured_filter = lambda _: _.disableADExpireDate and _.disableADExpireDate > now
- expiring_soon_filter = lambda _: _.disableADExpireDate and (_.disableADExpireDate - now).total_seconds() <= 60 * 60 * 24 * 15
- configured = len(filter(configured_filter, all_devs))
- expiringSoon= len(filter(expiring_soon_filter, all_devs))
- notConfigured = len(Device.get_devs_by_ownerId(str(self.id))) - configured
- return {
- 'configured': configured,
- 'expiringSoon': expiringSoon,
- 'notConfigured': notConfigured
- }
- def query_home_page_layout(self):
- menuDict = {}
- menu_features = []
- for menu, default_value in Const.MAIN_MENU_LIST.iteritems():
- if default_value:
- menu_features.append('hide_{}'.format(menu))
- else:
- menu_features.append(menu)
- menuDict[menu] = default_value
- homeDataDict = {}
- home_data_features = ['show_ad_income', 'show_offline_coins']
- for menu, default_value in Const.HOME_PAGE_DATA_LIST.iteritems():
- homeDataDict[menu] = default_value
- queryResult = self.query_feature_by_list(menu_features + home_data_features)
- for k, v in queryResult.items():
- if k in home_data_features:
- if k == 'show_ad_income':
- homeDataDict['today_ad_income'] = v
- elif k == 'show_offline_coins':
- homeDataDict['offline_coins'] = v
- else:
- if 'hide_' in k:
- if v:
- menu_name = k.replace('hide_', '')
- menuDict[menu_name] = False
- else:
- menu_name = k
- menuDict[menu_name] = v
- return menuDict, homeDataDict
- @property
- def service_phone(self):
- # 如果代理商接管客服电话,就显示代理商的服务电话
- if self.linkageSwitch.agentProxyServicePhone is True:
- agent = Agent.objects(id=self.agentId).first() # type: Optional[Agent]
- if agent and agent.service_phone:
- return agent.service_phone
- elif self.servicePhone:
- return self.servicePhone
- else:
- return self.username
- @property
- def my_agent(self):
- # type:()->Agent
- if not hasattr(self, '__my_agent__'):
- my_agent = Agent.objects(id=self.agentId).first()
- setattr(self, '__my_agent__', my_agent)
- return getattr(self, '__my_agent__')
- @property
- def force_follow_gzh(self):
- if self.forceFollowGzh == 'agent':
- isNeedFollow = self.my_agent.forceFollowGzh
- else:
- isNeedFollow = True if self.forceFollowGzh == 'yes' else False
- return isNeedFollow
- @property
- def show_auth_window(self):
- showAuth = 0 if self.supports('not_show_auth_window') else 1
- if showAuth == 0:
- return False
- else:
- return True
- @property
- def productAgent(self):
- if not hasattr(self, '__product_agent__'):
- agent = get_user_manager_agent(self)
- setattr(self, '__product_agent__', agent)
- return getattr(self, '__product_agent__')
- def freeze_ledger_balance(self, money, source_key, transaction_id):
- """
- 消费订单分账前的冻结
- """
- income_type = DEALER_INCOME_TYPE.DEVICE_INCOME
- self._freeze_balance(income_type, money, source_key, transaction_id, "inhandLedger")
- def clear_ledger_balance(self, transaction_id):
- """
- 分账记录收益完成 回滚
- """
- self._clear_frozen_balance(transaction_id, "inhandLedger")
- class DealerDict(dict):
- """
- 经销商的缓存表示
- """
- def __repr__(self):
- return '<DealerDict id=%s>' % (self.get('groupId', 'unknown'),)
- @property
- def v(self):
- return dict(self)
- @property
- def id(self):
- return self.get('id')
- @property
- def agentId(self):
- return self.get('agentId')
- @property
- def entity(self):
- if hasattr(self, '__entity__'):
- return getattr(self, '__entity__')
- dealer = Dealer.objects(id = self.id).first()
- if not dealer:
- raise Exception('no such dealer<id={}>'.format(self.id))
- else:
- setattr(self, '__entity__', dealer)
- return dealer
- @property
- def currency_mode(self):
- if 'currencyMode' not in self:
- Dealer.invalid_cache(self.id)
- dealer = Dealer.get_dealer(self.id) # type: DealerDict
- self.update(dealer.v)
- return self.get('currencyMode')
- @property
- def supportedConsumptionShow(self):
- """
- 客户端的 消费消息显示开关
- """
- if 'supportedConsumptionShow' not in self:
- Dealer.invalid_cache(self.id)
- dealer = Dealer.get_dealer(self.id)
- self.update(dealer.v)
- return self["supportedConsumptionShow"]
- def is_currency(self, left_group, right_group):
- # type: (GroupDict, GroupDict)->bool
- if left_group.ownerId != right_group.ownerId:
- return False
- if self.currency_mode == 'allNo':
- return False
- if self.currency_mode == 'allYes':
- return True
- if left_group.currencyGroup and right_group.currencyGroup and (
- left_group.currencyGroup == right_group.currencyGroup):
- return True
- else:
- return False
- def get_currency_group_ids(self, group, groups = None):
- # type: (Optional[Group, GroupDict], List[Optional[Group, GroupDict]])->list
- if self.currency_mode == 'allNo':
- return []
- else:
- if not groups:
- groups = Group.get_groups_of_dealer(str(self.id))
- if self.currency_mode == 'allYes':
- rv = []
- for item in groups: # type: GroupDict
- if item.groupId == group.groupId:
- continue
- rv.append(item.groupId)
- return rv
- else:
- if not group.currencyGroup:
- return []
- else:
- rv = []
- for item in groups: # type: GroupDict
- if item.groupId == group.groupId:
- continue
- if item.currencyGroup == group.currencyGroup:
- rv.append(item.groupId)
- return rv
- @property
- def servicePhone(self):
- return self['servicePhone']
- @property
- def my_agent(self):
- if '__my_agent__' not in self:
- my_agent = Agent.objects(id=self.agentId).first()
- self['__my_agent__'] = my_agent
- return self.get('__my_agent__')
- @property
- def username(self):
- return self['username']
- @property
- def nickname(self):
- return self['nickname']
- class UpscoreRecord(Document):
- logicalCode = StringField(verbose_name = "设备逻辑编号", default = '')
- devNo = StringField(verbose_name = "设备编号")
- devType = StringField(verbose_name = "设备类型", default = '')
- ownerId = StringField(verbose_name = "所有者", default = "")
- time = DateTimeField(verbose_name = "时间", default = datetime.datetime.now)
- score = IntField(verbose_name = "上分数量", default = 0)
- groupName = StringField(verbose_name = "上分分组名称", default = "")
- address = StringField(verbose_name = "上分设备地址", default = "")
- remark = StringField(verbose_name = "备注", default = "")
- package = DictField(verbose_name = "套餐", default = {})
- type = StringField(verbose_name = '上分类型(套餐上分,远程充卡),默认为空,表示上分', default = '')
- meta = {"collection": "UpscoreRecord", "db_alias": "logdata",
- # "shard_key":("ownerId",)
- }
- @classmethod
- def get_collection(cls):
- return cls._get_collection()
- class AdjustUserVirtualCardRecord(Document):
- cardId = StringField(verbose_name = "虚拟卡ID")
- cardNo = StringField(verbose_name = "虚拟卡卡号")
- adjustDays = IntField(verbose_name = "调整天数", default = 0)
- beforeAdjust = DateTimeField(verbose_name = "调整前的过期时间")
- adjustQuota = ListField(verbose_name = "调整的额度", default = [])
- oldQuota = ListField(verbose_name = "调整之前的额度", default = [])
- operator = StringField(verbose_name = "操作人")
- dealerId = StringField(verbose_name = "卡的管理者")
- adjustType = StringField(verbose_name = "调整方式", choices = TYPE_ADJUST_USER_VIRTUAL_CARD.choices())
- dateTimeAdded = DateTimeField(verbose_name = "添加时间", default = datetime.datetime.now)
- meta = {'collection': 'adjust_user_virtual_card_record', 'db_alias': 'logdata'}
- @classmethod
- def get_collection(cls):
- return cls._get_collection()
- class DealerRechargeRecord(Searchable):
- """
- 经销商充值记录
- """
- class PayState(IterConstant):
- UnPaid = 'UnPaid'
- Paid = 'Paid'
- Failure = 'Failure'
- Fake = 'Fake'
- Cancel = 'Cancel'
- Quit = 'Quit'
- Close = 'Close'
- class ProductType(IterConstant):
- SimCard = 'WF4801' # 流量卡充值
- ApiCost = 'WF48A' # API接入
- DisableAd = 'WF48D' # 禁用广告接入
- AutoSimCard = 'WF4802' # 流量卡自动充值
- ManualSimCard = 'WF4803' # 流量卡线下充值
- SimOrderVerify = 'WF5801' # 流量卡充值对账后重新分账
- ProductDesc = {
- ProductType.SimCard: cn(u'流量卡充值'),
- ProductType.AutoSimCard: cn(u'流量卡自动充值'),
- ProductType.ManualSimCard: cn(u'流量卡线下充值'),
- ProductType.ApiCost: cn(u'API配额充值'),
- ProductType.DisableAd: cn(u'纯净计划'),
- ProductType.SimOrderVerify: cn(u'流量卡充值对账'),
- }
- MapSubType = {
- DealerPaySubType.AUTO_SIM_CARD: ProductType.AutoSimCard,
- DealerPaySubType.MANUAL_SIM_CARD: ProductType.ManualSimCard,
- DealerPaySubType.SIM_CARD: ProductType.SimCard,
- DealerPaySubType.API_COST: ProductType.ApiCost,
- DealerPaySubType.DISABLE_AD: ProductType.DisableAd,
- DealerPaySubType.SIM_ORDER_VERIFY: ProductType.SimOrderVerify
- }
- MY_MAIN_TYPE = OrderMainType.PAY
- orderNo = StringField(verbose_name = u"订单号", unique = True)
- wxOrderNo = StringField(verbose_name = u"渠道订单号. 聚合商户,是聚合平台商户订单号;直连商户则是微信或支付宝订单号", default = "")
- transactionId = StringField(verbose_name = u"微信或者支付宝订单号", default = "")
- product = StringField(verbose_name = u'产品名称', default = ProductType.SimCard)
- items = ListField(verbose_name = u"充值商品", null = False)
- name = StringField(verbose_name = u'商品名称', default = '')
- dealerId = StringField(verbose_name = u"经销商ID", null = False)
- nickname = StringField(verbose_name = u"昵称", default = '')
- totalFee = IntField(verbose_name = u"订单金额(以分为单位)", min_value = 0, null = False)
- settleInfo = DictField(verbose_name = u'结算信息', default = {})
- status = StringField(verbose_name = u"充值结果", default = PayState.UnPaid)
- description = StringField(verbose_name = u"订单错误结果描述,一般为第三方错误码", default = "")
- createdTime = DateTimeField(default = datetime.datetime.now, verbose_name = u"生成时间")
- finishedTime = DateTimeField(default = None, verbose_name = u"支付完成时间")
- attachParas = DictField(verbose_name = u"消费模型信息", default = {})
- gateway = StringField(verbose_name = u'支付网关类型', default = '')
- payAppType = StringField(verbose_name = u'支付应用类型', default = '')
- payGatewayKey = StringField(verbose_name = u'支付网关key', default = '')
- withdrawSourceKey = StringField(verbose_name = u'提现网关source key', default = '')
- subject = StringField(verbose_name = u'商品标题', default = '')
- extraInfo = DictField(verbose_name = u"订单本身模型信息", default = None)
- meta = {'collection': 'dealer_recharge_record', 'db_alias': 'default',
- 'indexes': [
- {
- 'fields': ['dealerId', 'product']
- },
- 'orderNo', 'wxOrderNo', 'transactionId',
- # 'name'
- ]}
- search_fields = ('wxOrderNo', 'name')
- def __repr__(self):
- return '<DealerRechargeRecord id=%s>' % (str(self.id))
- @classmethod
- def get_product(cls, sub_type):
- return {
- 'type': cls.MapSubType[sub_type],
- 'desc': cls.ProductDesc[cls.MapSubType[sub_type]]
- }
- @classmethod
- def issue(cls, sub_type, payment_gateway, user, identifier=None, **payload):
- # type: (str, PaymentGateway, CapitalUser, basestring, Dict)->DealerRechargeRecord
- payload.update({
- 'product': cls.get_product(sub_type)['type'],
- 'gateway': payment_gateway.gateway_type,
- 'payAppType': payment_gateway.pay_app_type,
- 'payGatewayKey': payment_gateway.gateway_key,
- 'subject': cls.get_product(sub_type)['desc'],
- 'createdTime': datetime.datetime.now()
- })
- payload.update({
- 'withdrawSourceKey': payment_gateway.withdraw_source_key()
- })
- if 'extraInfo' not in payload:
- payload['extraInfo'] = {}
- payload['status'] = cls.PayState.UnPaid
- identifier = identifier if identifier else str(user.id)
-
- if 'orderNo' not in payload:
- payload['orderNo'] = OrderNoMaker.make_order_no_32(
- identifier = identifier, main_type = cls.MY_MAIN_TYPE, sub_type = sub_type)
- record = cls(**payload)
- record.save()
- return record
- @classmethod
- def get_record(cls, order_no, dealer_id = None):
- # type: (str, str)->Optional[DealerRechargeRecord]
- # TODO: 分片需要带上片键
- record = cls.objects(orderNo = order_no).first()
- return record
- def succeed(self, **kwargs):
- payload = {
- 'status': self.PayState.Paid
- }
- if kwargs:
- payload.update(kwargs)
- if 'finishedTime' not in payload:
- payload.update({'finishedTime': datetime.datetime.now()})
- result = DealerRechargeRecord.get_collection().update_one(
- filter = {'_id': ObjectId(self.id),
- 'status': {'$nin': [self.PayState.Paid, self.PayState.Close, self.PayState.Cancel]}},
- update = {'$set': payload},
- upsert = False
- )
- return result.matched_count == 1
- def fail(self, **kwargs):
- self.update(status = self.PayState.Failure, finishedTime = datetime.datetime.now(), **kwargs)
- return self.reload()
- def cancel(self):
- result = DealerRechargeRecord.get_collection().update_one(
- filter = {'_id': ObjectId(self.id), 'status': self.PayState.UnPaid},
- update = {'$set': {'status': self.PayState.Cancel,
- 'finishedTime': datetime.datetime.now()}},
- upsert = False
- )
- return result.matched_count == 1
- def quit(self):
- self.status = self.PayState.Quit
- self.save()
- return self
- def close(self, **kwargs):
- payload = {
- 'status': self.PayState.Close
- }
- if kwargs:
- payload.update(kwargs)
- result = self.get_collection().update_one(
- filter = {'_id': ObjectId(self.id),
- 'status': {'$nin': [self.PayState.Paid, self.PayState.Close, self.PayState.Cancel]}},
- update = {'$set': kwargs},
- upsert = False
- )
- return result.matched_count == 1
- @property
- def is_success(self):
- return self.status == self.PayState.Paid
- @property
- def is_fail(self):
- return self.status == self.PayState.Failure
- @property
- def is_cancel(self):
- return self.status == self.PayState.Cancel
- @property
- def is_unpay(self):
- return self.status == self.PayState.UnPaid
- @property
- def is_close(self):
- return self.status == self.PayState.Close
- @property
- def sum_of_price(self):
- # type: ()->RMB
- return sum_rmb([RMB(_['price']) for _ in self.items])
- def get_body(self):
- return self.ProductDesc.get(self.product)
- @property
- def fen_total_fee(self):
- return self.totalFee
- @property
- def my_gateway(self):
- return self.gateway
- @property
- def pay_app_type(self):
- return self.payAppType
- @property
- def pay_gateway_key(self):
- return self.payGatewayKey
- @property
- def withdraw_source_key(self):
- return self.withdrawSourceKey
- class RefundDealerRechargeRecord(RefundOrderBase):
- meta = {
- 'collection': 'refund_dealer_recharge_record',
- 'db_alias': 'default'
- }
- @classmethod
- def issue(cls, order, refundCash):
- # type:(DealerRechargeRecord, RMB)->RefundDealerRechargeRecord
- refund_order_no = OrderNoMaker.make_order_no_32(
- identifier = order.orderNo,
- main_type = OrderMainType.REFUND,
- sub_type = RefundSubType.REFUND)
- return cls(
- rechargeObjId = order.id,
- # refundSeq=next_seq,
- orderNo = refund_order_no,
- money = refundCash,
- status = cls.Status.CREATED,
- payAppType = order.pay_app_type).save()
-
- @property
- def pay_app_type(self):
- return self.payAppType
-
- @property
- def pay_sub_order(self):
- # type: ()->DealerRechargeRecord
- if not hasattr(self, '__pay_sub_order__'):
- pay_order = DealerRechargeRecord.objects(id = str(self.rechargeObjId)).first()
- setattr(self, '__pay_sub_order__', pay_order)
- return getattr(self, '__pay_sub_order__')
- @pay_sub_order.setter
- def pay_sub_order(self, order):
- setattr(self, '__pay_sub_order__', order)
- @classmethod
- def get_record(cls, **kwargs):
- return cls.objects(**kwargs).first()
- @property
- def notify_url(self):
- if self.pay_app_type in [PayAppType.WECHAT, PayAppType.WECHAT_MINI]:
- return REFUND_NOTIFY_URL.WECHAT_REFUND_BACK
- elif self.pay_app_type == PayAppType.JD_AGGR:
- return REFUND_NOTIFY_URL.JD_AGGRE_REFUND_BACK
- elif self.pay_app_type == PayAppType.JD_OPEN:
- return REFUND_NOTIFY_URL.JDOPEN_REFUND_BACK
- else:
- return None
-
- class OnSale(DynamicDocument):
- """
- 优惠活动管理
- """
- dealerId = StringField(verbose_name = "经销商ID", null = False)
- name = StringField(verbose_name = '活动名称', default = '')
- showSite = IntField(verbose_name = "展现的位置", default = 0) # 展现的位置0,表示扫码后的套餐页面。
- logicalCodeList = ListField(verbose_name = "需要推送的设备", null = False)
- onsaleType = StringField(verbose_name = '活动类型', default = '')
- detailDict = DictField(verbose_name = '活动详细数据', default = {})
- desc = StringField(verbose_name = '描述', default = '')
- img = StringField(verbose_name = '创意图片', default = '')
- onClickUrl = StringField(verbose_name = '回调地址', default = '')
- startTime = DateTimeField(verbose_name = '起始时间', default = datetime.datetime.now)
- endTime = DateTimeField(verbose_name = '结束时间', default = datetime.datetime.now)
- status = StringField(verbose_name = '状态', default = 'start')
- #: 展现类型,onlyOne,点击后就不弹出。forever,表示即使点击过,还是一直弹出,web:表示是页面,不是图片
- showType = StringField(verbose_name = '状态', default = 'onlyOne')
- onsaleTypeDict = {
- u'首次使用送金币': {
- 'onClickUrl': '/user/promotion/getCoins',
- 'desc': u'适用所有设备类型,用户首次扫码的时候,允许客户领金币进行体验,本次活动领过金币的用户,下次无法领取金币。',
- 'expression': "",
- 'img': '/app/img/marketing/first_give_coins.jpg',
- 'showType': 'onlyOne'
- },
- u'首次免费按摩': {
- 'onClickUrl': '/user/promotion/getDuration',
- 'desc': u'适用按摩坐垫以及足疗机,用户首次扫码的时候,允许客户直接启动设备体验,本次活动体验过的用户,下次无法再体验。',
- 'expression': "dev['devType']['code'] in ['100110','100111','100112','100113','100119','100900']",
- 'img': '/app/img/marketing/first_free.jpg',
- 'showType': 'onlyOne'
- },
- u'优惠充值大放送': {
- 'onClickUrl': '/user/onsaleRecharge',
- 'desc': u'适用所有设备类型,用户优惠充值,活动期间始终弹出此推广创意,即使用户已经充值优惠过了。(用户点击推广活动后,会跳转到对应设备的充值页面。需要提前在优惠设置中,把对应的地址下的充值套餐配置好)',
- 'expression': "",
- 'img': '/app/img/marketing/recharegeOnsale.jpg',
- 'showType': 'forever'
- },
- u'手机实名免费用': {
- 'onClickUrl': '/user/inputMobile',
- 'desc': u'首次验证手机号后,可以免费使用设备',
- 'expression': "",
- 'img': '/app/img/marketing/inputMobile.jpg',
- 'showType': 'onlyOne-web'
- },
- u'包年包月优惠多多': {
- 'onClickUrl': '/user/onsaleTicketList',
- 'desc': u'适用所有设备类型,用户优惠充值,活动期间始终弹出此推广创意,即使用户已经充值优惠过了。(用户点击推广活动后,会跳转到对应设备的充值页面。需要提前在优惠设置中,把对应的地址下的充值套餐配置好)',
- 'expression': "",
- 'img': '/app/img/marketing/recharegeOnsale.jpg',
- 'showType': 'forever'
- },
- }
- def to_dict(self):
- return {
- 'id': str(self.id),
- 'name': self.name,
- 'showSite': self.showSite,
- 'logicalCodeList': self.logicalCodeList,
- 'onsaleType': self.onsaleType,
- 'onClickUrl': self.onClickUrl,
- 'desc': self.desc,
- 'detailDict': self.detailDict,
- 'img': self.img,
- 'startTime': self.startTime.strftime("%Y-%m-%d"),
- 'endTime': self.endTime.strftime("%Y-%m-%d"),
- 'status': self.status
- }
- # 优惠活动访问记录
- class OnSaleRecord(DynamicDocument):
- onsaleId = StringField(verbose_name = "经销商ID", null = False)
- userId = StringField(verbose_name = "用户ID", null = False)
- nickName = StringField(verbose_name = "用户昵称", null = False)
- addedTime = DateTimeField(verbose_name = "访问时间", default = datetime.datetime.now)
- onsaleDetail = DictField(verbose_name = "活动详情数据", default = {})
- meta = {
- "collection": "on_sale_record",
- "db_alias": "logdata",
- }
- #: 经销商创建卡卷
- class VirtualCard(Searchable):
- cardName = StringField(verbose_name = "卡名称", default = "")
- ownerId = StringField(verbose_name = "卡的发布老板", default = "")
- groupIds = ListField(verbose_name = "可用使用卡的地址", default = []) # 空表示没有地址,*表示所有地址下可用
- devTypeList = ListField(verbose_name = "支持的设备类型清单", default = []) # 存放设备类型的ID
- price = MonetaryField(verbose_name = "卡的售价", default = RMB('0.00'))
- createTime = DateTimeField(verbose_name = "创建时间", default = datetime.datetime.now)
- periodDays = FloatField(verbose_name = "卡的可用天数", default = 30.0)
- expiredTime = DateTimeField(verbose_name="卡卷售卖下架时间", default=lambda : datetime.datetime.now() + datetime.timedelta(days=365))
- dayQuota = ListField(verbose_name = "日限额度", default = []) # {'unit':u'次','count':2}
- quota = ListField(verbose_name = "总的额度", default = [])
- userLimit = IntField(verbose_name = "用户数量限制", default = 10)
- userDesc = StringField(verbose_name = "用户侧的描述", default = "")
- dealerDesc = StringField(verbose_name = "经销商侧描述", default = "")
- status = IntField(verbose_name = "卡状态", default = 1) # -1:删除;0:停止发售;1:开始发售
- # 作为内置参数,接口不暴露出来 使用的时候后台脚本修改, 修改脚本名称为 adjust_virtual_card_need_renew
- needRenewMax = IntField(verbose_name = "过期后可续卡天数", default=10)
- needRenewMin = IntField(verbose_name = "过期前可续卡天数", default=10)
- # 暂停发售之后是否可以继续续卡
- renewIgnoreStatus = BooleanField(verbose_name = "暂停发售之后是否可以继续续卡", default = False)
- # 坤元虚拟卡特有:功率
- power = IntField(verbose_name = "包月卡功率限制", default = 0)
- meta = {
- "collection": "VirtualCard",
- "db_alias": "default",
- }
- @property
- def is_expired(self):
- return self.expiredTime <= datetime.datetime.now()
- @property
- def description(self):
- """
- 用于描述虚拟卡的信息
- :return:
- """
- try:
- qStr = u"额度:"
- for item in self.quota:
- qStr += "{} {}".format(item.get("count"), item.get("unit"))
- tStr = u"时间:{}天".format(self.periodDays)
- return "{}\t{}".format(qStr, tStr)
- except Exception:
- return None
- @classmethod
- def get_last(cls, ownerId):
- return cls.objects.filter(ownerId=ownerId).order_by("-id").first()
- @property
- def groups(self):
- if '*' in self.groupIds:
- groups = '*'
- else:
- groups = [{'groupName': grp['groupName'], 'address': grp['address'], 'groupId': grp['groupId']} for grp in
- Group.get_groups_by_group_ids(self.groupIds).values()]
- return groups
- @property
- def devTypes(self):
- devs = DeviceType.objects.filter(id__in=self.devTypeList).only("majorDeviceType", "name")
- devTypes = list()
- for _dev in devs: # type: DeviceType
- devTypes.append({"devTypeName": _dev.name, "majorDeviceType": _dev.majorDeviceType})
- return devTypes
- def to_dict(self):
- return {
- "id": str(self.id),
- "cardId": str(self.id),
- "cardName": self.cardName,
- "price": self.price,
- "periodDays": self.periodDays,
- "createTime": self.createTime,
- "expiredTime": self.expiredTime,
- "userDesc": self.userDesc,
- "dealerDesc": self.dealerDesc,
- "status": self.status,
- 'isExpired': self.is_expired,
- 'power': self.power,
- "quota": self.quota,
- "dayQuota": self.dayQuota,
- "groups": self.groups,
- "devTypes": self.devTypes,
- 'userLimit': self.userLimit
- }
- class ElecPriceTemplate(Searchable):
- ownerId = StringField(verbose_name = "卡的发布老板", default = "")
- name = StringField(verbose_name = "卡的发布老板", default = "")
- priceList = ListField(verbose_name = "48分时价格", default = [])
- class ItemType(Searchable):
- ownerId = StringField(verbose_name = "ownerId", default = "")
- title = StringField(verbose_name = "标题", default = "")
- desc = StringField(verbose_name = "描述", default = "")
- picUrl = StringField(verbose_name = "图片", default = "")
- price = IntField(verbose_name = "价格,分", default = 1)
- class SubAccount(UserSearchable):
- """
- 子账号表
- """
- agentId = StringField(verbose_name = "agentId", min_length = 1)
- phone = StringField(verbose_name = "电话", default = "")
- bossId = ObjectIdField(verbose_name = "主账号ID")
- permissionList = ListField(verbose_name = "菜单清单", default = [])
- meta = {
- 'collection': 'sub_account',
- 'db_alias': 'default',
- 'indexes': [
- {
- 'fields': ['username', 'bossId'], 'unique': True
- },
- {
- 'fields': ['username', 'agentId'], 'unique': True
- }
- ],
- }
- def __str__(self):
- return '{}<id={} username={} nickname={} agentId={}>'.format(
- self.__class__.__name__, str(self.id), self.username, self.nickname, self.agentId)
- @property
- def myBoss(self):
- # type:()->Dealer
- if hasattr(self, '_myBoss'):
- return getattr(self, '_myBoss')
- else:
- myBoss = Dealer.objects.get(id = self.bossId)
- setattr(self, '_myBoss', myBoss)
- return myBoss
- def query_feature_by_list(self, queryList):
- return self.myBoss.query_feature_by_list(queryList)
- def get_feature(self, feature_name):
- return self.myBoss.get_feature(feature_name)
- def query_home_page_layout(self):
- return self.myBoss.query_home_page_layout()
- def to_dict(self):
- value = self.myBoss.to_dict()
- value.update(
- {
- 'username': self.username,
- 'nickname': self.nickname,
- 'role': ROLE.subaccount
- }
- )
- return value
- @property
- def switches(self):
- return self.myBoss.switches
- @property
- def isManagerialOpenIdBound(self):
- return self.myBoss.isManagerialOpenIdBound
- def get_bound_pay_openid(self, key):
- return self.myBoss.get_bound_pay_openid(key)
- @property
- def format_default_discount(self):
- return self.myBoss.format_default_discount
- def get_own_devices(self):
- groupIds = Group.get_group_ids_of_dealer(str(self.bossId))
- devNoList = Device.get_devNos_by_group(groupIds)
- return Device.get_dev_by_nos(devNoList).values()
- @property
- def income_aggregate_source(self):
- # type: ()->Dict[str, AnyStr]
- """
- e.g. {'ad': u'广告'}
- :return:
- """
- g = itemgetter(*self.myBoss.supportedIncomeAggregate)
- return dict(zip(self.myBoss.supportedIncomeAggregate, g(DEALER_INCOME_SOURCE_TRANSLATION)))
- @property
- def consumption_aggregate_source(self):
- # type: ()->Dict[str, AnyStr]
- """
- e.g. {'elect': u'电量'}
- :return:
- """
- g = itemgetter(*self.myBoss.supportedConsumptionAggregate)
- return dict(zip(self.myBoss.supportedConsumptionAggregate, g(DEALER_CONSUMPTION_AGG_KIND_TRANSLATION)))
- def set_agent_profit_share(self, share):
- # type: (Percent)->int
- """
- :param share:
- :return:
- """
- updated = self.myBoss.update(agentProfitShare = share.mongo_amount)
- return updated
- @property
- def adShow(self):
- return self.myBoss.adShow
- @property
- def ad_show(self):
- return self.myBoss.ad_show
- @property
- def pushBrokerUrl(self):
- return self.myBoss.pushBrokerUrl
- @property
- def isPurePartner(self):
- return self.myBoss.isPurePartner
- @property
- def wechat(self):
- return self.myBoss.wechat
- @property
- def description(self):
- return self.myBoss.description
- @property
- def cardNo(self):
- return self.myBoss.cardNo
- @property
- def bankName(self):
- return self.myBoss.bankName
- @property
- def cardHolder(self):
- return self.myBoss.cardHolder
- @property
- def isRead(self):
- return self.myBoss.isRead
- @property
- def noPassReason(self):
- return self.myBoss.noPassReason
- @property
- def cibMerchant(self):
- return self.myBoss.cibMerchant
- @property
- def managerialAppId(self):
- return self.myBoss.managerialAppId
- @property
- def managerialOpenId(self):
- return self.myBoss.managerialOpenId
- @property
- def wechatAuthUserInfo(self):
- return self.myBoss.wechatAuthUserInfo
- @property
- def managerialWechatBoundTime(self):
- return self.myBoss.managerialWechatBoundTime
- @property
- def agentProfitShare(self):
- return self.myBoss.agentProfitShare
- @property
- def beforeCharge(self):
- return self.myBoss.beforeCharge
- @property
- def noRecharge(self):
- return self.myBoss.noRecharge
- @property
- def defaultWashConfig(self):
- return self.myBoss.defaultWashConfig
- @property
- def withdrawlNotify(self):
- return self.myBoss.withdrawlNotify
- @property
- def offlineNotify(self):
- return self.myBoss.offlineNotifySwitch
- @property
- def dailyIncomeReportPushSwitch(self):
- return self.myBoss.dailyIncomeReportPushSwitch
- @property
- def newUserPaymentOrderPushSwitch(self):
- return self.myBoss.newUserPaymentOrderPushSwitch
- @property
- def devFaultPushDealerSwitch(self):
- return self.myBoss.devFaultPushDealerSwitch
- @property
- def devFaultPushUserSwitch(self):
- return self.myBoss.devFaultPushUserSwitch
- @property
- def currency_mode(self):
- return self.myBoss.currency_mode
- @property
- def serviceName(self):
- return self.myBoss.serviceName
- @property
- def servicePhone(self):
- return self.myBoss.servicePhone
- @property
- def qrcodeUrl(self):
- return self.myBoss.qrcodeUrl
- @property
- def withdrawFeeRatio(self):
- return self.myBoss.withdrawFeeRatio
- @property
- def annualTrafficCost(self):
- return self.myBoss.annualTrafficCost
- @property
- def limitAdLocation(self):
- return self.myBoss.limitAdLocation
- @property
- def defaultDiscountConfig(self):
- return self.myBoss.defaultDiscountConfig
- @property
- def supportedIncomeAggregate(self):
- return self.myBoss.supportedIncomeAggregate
- @property
- def supportedConsumptionAggregate(self):
- return self.myBoss.supportedConsumptionAggregate
- @property
- def bottomAd(self):
- return self.myBoss.bottomAd
- @property
- def limitDevNum(self):
- return self.myBoss.limitDevNum
- @property
- def feature_boolean_map(self):
- return self.myBoss.feature_boolean_map
- @property
- def features(self):
- return self.myBoss.features
- @property
- def normal(self):
- my_boss = self.myBoss # type: Dealer
- if not my_boss.normal:
- return my_boss.normal
- else:
- return self.status == self.Status.NORMAL
- @property
- def forbidden(self):
- my_boss = self.myBoss # type: Dealer
- if my_boss.forbidden:
- return my_boss.forbidden
- else:
- return self.status == self.Status.FORBIDDEN
- @property
- def abnormal(self):
- my_boss = self.myBoss # type: Dealer
- if my_boss.abnormal:
- return my_boss.abnormal
- else:
- return self.status == self.Status.ABNORMAL
- @property
- def no_withdraw(self):
- return False
- @property
- def is_inhouse_wallet(self):
- return False
- def limit_filter_date(self, startTime, endTime):
- my_boss = self.myBoss # type: Dealer
- return my_boss.limit_filter_date(startTime, endTime)
- @property
- def service_phone(self):
- my_boss = self.myBoss # type: Dealer
- return my_boss.service_phone
- class Complaint(Searchable):
- openId = StringField(verbose_name = 'openId', default = '')
- logicalCode = StringField(verbose_name = 'logicalCode', default = '')
- groupName = StringField(verbose_name = 'groupName', default = '')
- orderNo = StringField(verbose_name = 'orderNo', default = '')
- handledStatus = StringField(verbose_name = 'status', default = 'init') # init/notice/warning/forbidden
- reason = StringField(verbose_name = 'reason', default = '')
- dateTimeAdded = DateTimeField(verbose_name = u'添加进来的时间', default = datetime.datetime.now)
- handledTime = DateTimeField(verbose_name = u'添加进来的时间', default = datetime.datetime.now)
- meta = {'collection': 'complaint', 'db_alias': 'logdata'}
- # 发送给经销商的消息
- class DealerMessage(Searchable):
- ownerId = StringField(verbose_name = 'ownerId', default = '')
- fromUser = StringField(verbose_name = 'fromUser', default = '')
- messageType = StringField(verbose_name = 'messageType', default = '')
- title = StringField(verbose_name = 'title', default = '')
- desc = StringField(verbose_name = 'desc', default = '')
- read = BooleanField(verbose_name = 'status', default = False)
- dateTimeAdded = DateTimeField(verbose_name = u'添加时间', default = datetime.datetime.now)
- readTime = DateTimeField(verbose_name = u'添加时间', default = datetime.datetime.now)
- relatedInfo = DictField(verbose_name = u'关联信息,便于查找,比如投诉信息', default = {})
- meta = {'collection': 'dealer_message', 'db_alias': 'logdata'}
- # 经销商的换货单
- class ExchangeOrder(Searchable):
- factoryId = ObjectIdField(verbose_name = 'factoryId', default = '')
- agentId = ObjectIdField(verbose_name = 'agentId', default = '')
- ownerId = ObjectIdField(verbose_name = 'ownerId', default = '')
- dateTimeAdded = DateTimeField(verbose_name = 'dateTimeAdded', default = datetime.datetime.now())
- dealerStatus = StringField(verbose_name = u'经销商侧状态', default = '') # 比如已提单、已发货等
- partIds = ListField(verbose_name = u'换货单', default = [])
- pics = ListField(verbose_name = u'用户上传的图片', default = [])
- dealerWords = StringField(verbose_name = u'经销商留言', default = '')
- factoryAddr = DictField(verbose_name = u'厂家维修地址', default = {})
- dealerOrderNo = StringField(verbose_name = u'经销商的快递单号', default = '')
- factoryStatus = StringField(verbose_name = u'厂家侧的状态', default = '') # 比如已发货、已同意换货等
- factoryOrderNo = StringField(verbose_name = u'厂家的快递单号', default = '')
- dealerAddr = DictField(verbose_name = u'经销商地址', default = {})
- factoryWords = StringField(verbose_name = u'厂家留言', default = '')
- more = StringField(verbose_name = u'备注', default = '')
- meta = {'collection': 'exchange_order', 'db_alias': 'default'}
- search_fields = ('dealerWords', 'factoryWords', 'factoryOrderNo', 'dealerOrderNo')
- def make_status_info(self):
- dealerInfo = ''
- factoryInfo = ''
- if self.dealerStatus == 'created':
- dealerInfo = u'经销商申请换板'
- elif self.dealerStatus == 'sended':
- dealerInfo = u'经销商已发货'
- if self.factoryStatus == 'agreed':
- factoryInfo = u'厂家同意换货'
- elif self.factoryStatus == 'disagreed':
- factoryInfo = u'厂家不同意'
- elif self.factoryStatus == 'sended':
- factoryInfo = u'售后中心已发货'
- elif self.factoryStatus == 'closed':
- factoryInfo = u'成功关闭'
- elif self.factoryStatus == '':
- factoryInfo = u'待确认'
- if dealerInfo and factoryInfo:
- return '%s,%s' % (dealerInfo, factoryInfo)
- elif dealerInfo:
- return dealerInfo
- else:
- return factoryInfo
- # 卡的上分记录表
- class UpCardScoreRecord(Searchable):
- cardId = StringField(verbose_name = "cardId", default = '')
- cardNo = StringField(verbose_name = "cardNo", default = '')
- ownerId = StringField(verbose_name = "所有者", default = "")
- dateTimeAdded = DateTimeField(verbose_name = "时间", default = datetime.datetime.now)
- score = FloatField(verbose_name = "上分数量", default = 0.0)
- address = StringField(verbose_name = "绑定地址", default = "")
- remark = StringField(verbose_name = "备注", default = "")
- meta = {"collection": "UpCardScoreRecord", "db_alias": "logdata"}
- @classmethod
- def get_collection(cls):
- return cls._get_collection()
- class PermissionInfo(Searchable): # 权限模版
- pid = StringField(verbose_name = 'pid', null = True, auto_incre = True)
- key = StringField(verbose_name = 'key', unique = True, null = False)
- value = BooleanField(verbose_name = 'value', default = False)
- name = StringField(verbose_name = '权限名称', default = '')
- desc = StringField(verbose_name = '权限详情', default = '')
- dateTimeAdded = DateTimeField(verbose_name = '添加时间', default = datetime.datetime.now)
- meta = {
- 'collection': 'permission_info',
- 'db_alias': 'default',
- }
- @classmethod
- def get_permissions(cls):
- def run(head):
- if head.count() == 0:
- return
- res = {}
- for item in head:
- value = run(all_Permission.filter(pid = str(item.id))) # 结果为None or dict
- res[item.key] = value or item.value
- return res
- all_Permission = cls.objects.all()
- head = all_Permission.filter(pid = None)
- return run(head)
- class PermissionRole(Searchable): # 成员表
- AUTHORIZE_TYPE = ['dealer_to_dealer', 'dealer_to_subAccount']
- roleName = StringField(verbose_name='角色名称', null=True, default='')
- operId = StringField(verbose_name='操作人ID')
- dealerId = StringField(verbose_name='经销商ID')
- subAccountId = StringField(verbose_name='子账号ID')
- permissionRuleId = StringField(verbose_name='权限配置')
- dateTimeAdded = DateTimeField(verbose_name='添加时间', default=datetime.datetime.now)
- lastModifiedTime = DateTimeField(verbose_name='最后一次操作时间', default=datetime.datetime.now)
- authorizeType = StringField(verbose_name='授权关系',choices = AUTHORIZE_TYPE)
- isActive = BooleanField(verbose_name='激活', default=False)
- meta = {
- 'collection': 'permission_role',
- 'db_alias': 'default',
- }
- @staticmethod
- def add_dealer_role(oper_id, dealerId):
- rule = PermissionRule.objects.create(ruleName=str(uuid.uuid4()), dealerId=dealerId,
- permissionDict=PermissionInfo.get_permissions())
- models = {
- 'roleName': None,
- 'operId': oper_id,
- 'dealerId': dealerId,
- 'permissionRuleId': str(rule.id),
- 'authorizeType': 'dealer_to_dealer',
- }
- return PermissionRole.objects.create(**models)
- @staticmethod
- def add_subAccount_role(oper_id, dealerId):
- rule = PermissionRule.objects.create(ruleName=str(uuid.uuid4()), dealerId=dealerId,
- permissionDict=PermissionInfo.get_permissions())
- models = {
- 'roleName': None,
- 'operId': oper_id,
- 'dealerId': dealerId,
- 'permissionRuleId': str(rule.id),
- 'authorizeType': 'dealer_to_subAccount',
- }
- return PermissionRole.objects.create(**models)
- def save(self, **kw):
- self.lastModifiedTime = datetime.datetime.now()
- return super(PermissionRole, self).save(**kw)
- @classmethod
- def get_role_permission(cls, dealerId, operId):
- cacheKey = '{}-{}'.format(dealerId, operId)
- permissionRule = cache.get(cacheKey)
- if not permissionRule:
- role = cls.objects.filter(dealerId=dealerId, operId=operId, isActive=True).first()
- permissionRule = {}
- if role:
- permissionRule = PermissionRule.objects.get(id=role.permissionRuleId).permissionDict
- cache.set(cacheKey, permissionRule)
- return permissionRule
- @classmethod
- def delete_role_permission(cls, operIds, dealerId):
- cls.objects.filter(operId__in=operIds, dealerId=dealerId).update(isActive=False)
- for operId in operIds:
- cacheKey = '{}-{}'.format(dealerId, operId)
- cache.delete(cacheKey)
- @classmethod
- def get_auth_to_dealer(cls, dealerId):
- """
- 获取 本账号授权的经销商
- :param dealerId:
- :return:
- """
- return cls.objects.filter(dealerId=dealerId, authorizeType='dealer_to_dealer', isActive=True).values_list(
- 'operId')
- @classmethod
- def get_auth_to_sub(cls, dealerId):
- """
- 获取 本账号授权的子账号
- :param dealerId:
- :return:
- """
- return cls.objects.filter(dealerId=dealerId, authorizeType='dealer_to_subAccount', isActive=True).values_list(
- 'operId')
- @classmethod
- def get_is_auth_list(cls, operId, authorizeType):
- """
-
- :param operId: 操作人ID
- :param authorizeType: 授权类型
- :return:
- """
- return cls.objects.filter(operId=str(operId), authorizeType=authorizeType, isActive=True).values_list('dealerId')
- class PermissionRule(Searchable): # 权限配置表
- ruleName = StringField(verbose_name = '配置名称')
- dealerId = StringField(verbose_name = '经销商')
- permissionDict = DictField(verbose_name = '权限内容')
- dateTimeAdded = DateTimeField(verbose_name = '添加时间', default = datetime.datetime.now)
- lastModifiedTime = DateTimeField(verbose_name = '最后一次操作时间', default = datetime.datetime.now)
- meta = {
- 'collection': 'permission_rule',
- 'db_alias': 'default',
- }
- def get_permissionDict(self):
- def update_permission(base_dict, update_dict):
- result = {}
- for key, value in base_dict.items():
- if isinstance(value, dict):
- res = update_permission(value, update_dict[key])
- result[key] = res
- else:
- # print key,value,update_dict
- if not isinstance(update_dict, dict):
- result[key] = update_dict
- else:
- if key in update_dict:
- result[key] = update_dict[key]
- else:
- result[key] = value
- return result
- return update_permission(PermissionInfo.get_permissions(), self.permissionDict)
- def save(self, **kw):
- self.lastModifiedTime = datetime.datetime.now()
- return super(PermissionRule, self).save(**kw)
- class TodoMessage(Searchable):
- title = StringField(verbose_name=u"标题")
- content = StringField(verbose_name=u"内容")
- type = IntField(verbose_name=u"待办的种类", choices=TodoTypeEnum.choices())
- link = StringField(verbose_name=u"跳转地址")
- done = IntField(verbose_name=u"待办状态", choices=TodoDone.choices(), default=TodoDone.INIT)
- ownerId = StringField(verbose_name=u"经销商")
- expiredTime = DateTimeField(verbose_name=u"信息过期时间")
- dateTimeAdded = DateTimeField(verbose_name=u"信息添加的时间", default=datetime.datetime.now())
- popOnlyOnce = BooleanField(verbose_name=u"登录后台只显示一次", default=True)
- def to_dict(self):
- return {
- "id": self.id,
- "title": self.title,
- 'content': self.content,
- "type": self.type,
- "link": self.link,
- "status": self.done,
- "ownerId": self.ownerId,
- 'popOnlyOnce': self.popOnlyOnce
- }
- def has_done(self):
- self.update(done=TodoDone.DONE)
- @property
- def checkModel(self):
- for _ in TodoTypeEnum:
- if _ == self.type:
- return _._load_todoCls()
- @classmethod
- def get_todo_message(cls, user, typeList = TodoTypeEnum.choices()):
- """
- 获取所有未完成的任务
- """
- items = cls.objects.filter(
- ownerId = str(user.id),
- done__in = [TodoDone.INIT, TodoDone.ING],
- type__in = typeList,
- expiredTime__gt = datetime.datetime.now())
- dataList = list()
- for _m in items: # type: TodoMessage
- checkModel = _m.checkModel
- # 进行一次任务检查 查看任务是否完成以及是否需要强制执行
- hasDone, force = checkModel.check_has_done(_m)
- if hasDone:
- _m.has_done()
- continue
- data = _m.to_dict()
- data["force"] = force
- dataList.append(data)
- return dataList
- @classmethod
- def sim_expire_message(cls, ownerId, expireCount):
- params = {
- 'type': 'simCard',
- 'redirect': concat_front_end_url(uri = '/app/deviceCard.html')
- }
- link = add_query(concat_server_end_url(uri = '/dealer/wechat/entry'), params)
- msg = cls(
- id = "1",
- title = u"流量卡续费提醒",
- content = u"您当前有{}台设备流量卡需要续费,为了避免影响设备的正常运行,请您尽快续费(已经充值请忽略)".format(expireCount),
- type = TodoTypeEnum.SMS_TODO.code,
- link = link,
- ownerId = ownerId,
- done = int(TodoDone.ING),
- expiredTime = datetime.datetime.now() + datetime.timedelta(days = 365),
- popOnlyOnce = False
- ).to_dict()
- return msg
|