models.py 110 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. """
  4. web.dealer.models
  5. ~~~~~~~~~
  6. """
  7. import datetime
  8. import logging
  9. import uuid
  10. from operator import itemgetter
  11. from bson.objectid import ObjectId
  12. from django.conf import settings
  13. from django.core.cache import cache
  14. from mongoengine import EmbeddedDocument, StringField, BooleanField, EmbeddedDocumentField, MapField, DynamicDocument, \
  15. DateTimeField, Document, IntField, ListField, DictField, FloatField, ObjectIdField, EmbeddedDocumentListField, \
  16. LazyReferenceField, DynamicEmbeddedDocument, GenericLazyReferenceField
  17. from typing import Dict, AnyStr, Any, List, Optional, TYPE_CHECKING
  18. from apilib.monetary import RMB, Percent, sum_rmb, Permillage
  19. from apilib.systypes import IterConstant, StrEnum
  20. from apilib.utils_string import cn, md5
  21. from apilib.utils_url import add_query
  22. from apps.web.agent.models import Agent, MoniAppPoint
  23. from apps.web.common.models import UserSearchable, MonthlyPackageTemp
  24. from apps.web.common.models import WithdrawRecord, CapitalUser, Balance
  25. from apps.web.common.transaction import OrderNoMaker, OrderMainType, DealerPaySubType, WITHDRAW_PAY_TYPE, RefundSubType
  26. from apps.web.constant import TYPE_ADJUST_USER_VIRTUAL_CARD, DEALER_CONSUMPTION_AGG_KIND, \
  27. DEALER_CONSUMPTION_AGG_KIND_TRANSLATION, Const, APP_TYPE, AppPlatformType
  28. from apps.web.core import PayAppType, ROLE, APP_KEY_DELIMITER
  29. from apps.web.core.db import Searchable, MonetaryField, PercentField, PermillageField
  30. from apps.web.core.exceptions import UpdateError, ServiceException, InvalidParameter, MerchantError
  31. from apps.web.dealer.constant import TodoDone, TodoTypeEnum, LinkageSwitchEnum
  32. from apps.web.core.messages.sms import dealerWithdrawSMSProvider, dealerMonitorWithdrawSMSProvider
  33. from apps.web.core.models import BoundOpenInfo, BankCard, SaobeiPayApp, JDAggrePayApp, DlbPayApp, \
  34. YsPayApp, RuralCreditUnionApp, SwapPayApp, JDOpenPayApp, MERCHANT_APP_LIST
  35. from apps.web.core.payment import WithdrawGateway
  36. from apps.web.dealer.define import DEALER_INCOME_SOURCE, DEALER_INCOME_TYPE, DealerConst, \
  37. DEALER_INCOME_SOURCE_TRANSLATION
  38. from apps.web.device.models import Device, Group, GroupDict, DeviceType
  39. from apps.web.helpers import get_app, get_user_manager_agent
  40. from apps.web.utils import concat_dealer_access_entry_url, concat_front_end_url, concat_server_end_url
  41. if TYPE_CHECKING:
  42. from apps.web.common.transaction import WithdrawHandler
  43. from apps.web.core.payment import PaymentGateway
  44. from apps.web.core import PayAppBase
  45. from apps.web.merchant.models import JDOpenApplyInfo, MerchantSourceInfo
  46. PayAppT = Optional[JDAggrePayApp, SaobeiPayApp, YsPayApp, RuralCreditUnionApp, DlbPayApp, JDOpenPayApp]
  47. logger = logging.getLogger(__name__)
  48. class DealerCacheMgr():
  49. pass
  50. class DealerAddr(EmbeddedDocument):
  51. id = StringField(verbose_name = 'id', default = str(uuid.uuid4()))
  52. default = BooleanField(verbose_name = u'是否默认地址', default = False)
  53. name = StringField(verbose_name = 'name', default = '')
  54. tel = StringField(verbose_name = 'telno', default = '')
  55. addr = StringField(verbose_name = 'addr', default = '')
  56. class ApiAppInfo(DynamicDocument):
  57. people = StringField(verbose_name=u"联系人", default='')
  58. tel = StringField(verbose_name=u"联系电话", default='')
  59. callbackUrl = StringField(verbose_name=u"回调地址Url", default='')
  60. apiDeviceMax = IntField(verbose_name=u'API最大接入数量', default=0)
  61. apiDevicePerCost = MonetaryField(verbose_name=u'API配额单价', default=RMB(50))
  62. meta = {
  63. 'collection': 'api_app_info',
  64. 'db_alias': 'default'
  65. }
  66. class DisableAdPlan(DynamicDocument):
  67. cycle = IntField(verbose_name=u'单次购买周期(单位:月)', default=12)
  68. disableAdCost = MonetaryField(verbose_name=u'纯净版单价', default=RMB(20))
  69. meta = {
  70. 'collection': 'disable_ad_plan',
  71. 'db_alias': 'default'
  72. }
  73. class ConfigTemplate(EmbeddedDocument):
  74. rechargeDiscount = ListField(verbose_name=u'优惠充值模板', default=[])
  75. cardRechargeDiscount = ListField(verbose_name=u'卡优惠充值模板', default=[])
  76. class DealerLinkageSwitch(DynamicEmbeddedDocument):
  77. """
  78. 经销商相关开关项
  79. """
  80. chargeInsurance = BooleanField(default=False, verbose_name=u"充电险开关")
  81. agentProxyServicePhone = BooleanField(verbose_name=u'代理商是否接管客服', default=False)
  82. def to_dict(self):
  83. return {"chargeInsurance": self.chargeInsurance, 'agentProxyServicePhone': self.agentProxyServicePhone}
  84. class Dealer(CapitalUser):
  85. """
  86. 经销商
  87. """
  88. INCOME_SOURCE_LIST = DEALER_INCOME_SOURCE.choices()
  89. INCOME_SOURCE_TO_TYPE = DealerConst.MAP_SOURCE_TO_TYPE
  90. INCOME_TYPE_LIST = DEALER_INCOME_TYPE.choices()
  91. INCOME_TYPE_TO_FIELD = DealerConst.MAP_TYPE_TO_FIELD
  92. #: 默认管理平台微信公众号授权用户信息
  93. DEFAULT_WECHAT_AUTH_USER_INFO = {
  94. 'avatar': '',
  95. 'sex': 0
  96. }
  97. DEFAULT_CHECKPOINT = {
  98. 'gerenzhongxin': False,
  99. 'yue': False,
  100. 'baogaolaoban': False,
  101. 'fukuan': False
  102. }
  103. DEFAULT_PAY_TYPE = {
  104. ROLE.myuser: {
  105. AppPlatformType.ALIPAY: PayAppType.ALIPAY,
  106. AppPlatformType.WECHAT: PayAppType.WECHAT,
  107. AppPlatformType.JD: PayAppType.JD_AGGR,
  108. AppPlatformType.WECHAT_MINI: PayAppType.WECHAT_MINI
  109. }
  110. }
  111. CACHE_KEY = 'dealer-info-{id}'
  112. CacheMgr = DealerCacheMgr
  113. wechat = StringField(verbose_name = "微信", default = "")
  114. description = StringField(verbose_name = "描述", default = "")
  115. phone = StringField(verbose_name = "电话", default = "")
  116. # 相当于 经销商的资金池
  117. deviceBalance = MapField(EmbeddedDocumentField(document_type=Balance))
  118. # 经销商获取的分账收益 在消费订单结束的时候进行记录
  119. ledgerBalance = MapField(EmbeddedDocumentField(document_type=Balance))
  120. adBalance = MapField(EmbeddedDocumentField(document_type=Balance))
  121. payOpenIdMap = MapField(EmbeddedDocumentField(BoundOpenInfo))
  122. cardNo = StringField(verbose_name = "银行卡号", default = "")
  123. bankName = StringField(verbose_name = "银行名称", default = "")
  124. cardHolder = StringField(verbose_name = "持卡人名称", default = "")
  125. isRead = StringField(verbose_name = "是否已经读了消息", default = "N")
  126. noPassReason = StringField(verbose_name = "未通过原因", default = "")
  127. cibMerchant = BooleanField(verbose_name = "是否立即生效", default = False)
  128. # 推送消息
  129. managerialAppId = StringField(verbose_name = "auth,notify app id", default = "")
  130. managerialOpenId = StringField(verbose_name = "后台,接受推送消息", default = "")
  131. wechatAuthUserInfo = DictField(verbose_name = "经销商授权后获取的用户信息", default = DEFAULT_WECHAT_AUTH_USER_INFO)
  132. managerialWechatBoundTime = DateTimeField(verbose_name = "绑定微信的时间")
  133. agentId = StringField(verbose_name = "从属于哪个代理商", null = False)
  134. # 上级代理商设置的经营分成比例策略
  135. agentProfitShare = PercentField(verbose_name='代理商设备运营分成比例', default = Percent('0.00'))
  136. agentMerProfitShare = PercentField(verbose_name='代理商设备运营分成比例 商户收款', default = Percent('0.00'))
  137. #: 默认选项|开关
  138. #: 选项
  139. adShow = BooleanField(verbose_name = "广告显示与否的选项", default = True)
  140. noAdPolicy = StringField(verbose_name = "不打开广告情况下的显示策略(notshow,noshow,banner)", default = 'noshow')
  141. beforeCharge = BooleanField(verbose_name = "使用前必须充值开关", default = False)
  142. noRecharge = BooleanField(verbose_name = "不需要充值功能", default = False)
  143. defaultWashConfig = DictField(verbose_name = "与经销商绑定的washconfig", default = {})
  144. #: 通知选项
  145. withdrawlNotify = BooleanField(verbose_name = "提现通知", default = False)
  146. offlineNotifySwitch = BooleanField(verbose_name = "设备离线通知开关", default = False)
  147. offlineNotifyTime = StringField(verbose_name = '设备离线通知时间', default = '')
  148. dailyIncomeReportPushSwitch = BooleanField(verbose_name = '是否开启次日9点推送昨日收益报表', default = False)
  149. newUserPaymentOrderPushSwitch = BooleanField(verbose_name = '是否开启', default = False)
  150. devFaultPushDealerSwitch = BooleanField(verbose_name = '设备故障通知开关,是否推送给经销商', default = False)
  151. devFaultPushUserSwitch = BooleanField(verbose_name = '设备故障通知开关,是否推送给用户', default = False)
  152. #: 用于客服的联系方式
  153. serviceName = StringField(verbose_name = "客服名称", default = "", max_length = 32)
  154. servicePhone = StringField(verbose_name = "客服电话", default = "", max_length = 32)
  155. qrcodeUrl = StringField(verbose_name = "二维码图片链接", default = "", max_length = 256)
  156. # 提现费率. 由代理商设置
  157. withdrawFeeRatio = PermillageField(
  158. verbose_name = '提现费率',
  159. default = Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO,
  160. min_value = Const.MIN_DEALER_WITHDRAW_FEE_RATIO,
  161. max_value = Const.MAX_DEALER_WITHDRAW_FEE_RATIO
  162. )
  163. # 流量卡年费
  164. annualTrafficCost = MonetaryField(default = Const.PLATFORM_DEFAULT_TRAFFIC_COST, verbose_name = '流量卡年费')
  165. # 该值由平台调整. 可以单独调整某些经销商的成本价格和卡年费
  166. trafficCardCost = MonetaryField(verbose_name = u'平台提供给经销商的流量卡成本价', default = None)
  167. limitAdLocation = BooleanField(default = False, verbose_name = u'是否限制广告领取的地域')
  168. features = ListField(verbose_name = u'支持的特性', default = [])
  169. defaultDiscountConfig = DictField(verbose_name = u"经销商默认优惠充值套餐", default = Const.DEFAULT_DISCOUNT_RULE)
  170. defaultCardDiscountConfig = DictField(verbose_name = u"经销商默认卡优惠充值套餐", default = Const.DEFAULT_DISCOUNT_RULE)
  171. supportedIncomeAggregate = ListField(verbose_name = u'支持的收入聚合', default = DEALER_INCOME_SOURCE.choices())
  172. supportedConsumptionAggregate = ListField(verbose_name=u'支持的消费聚合', default=DEALER_CONSUMPTION_AGG_KIND.choices())
  173. supportedConsumptionShow = ListField(verbose_name=u"用户端的消费信息显示", default=[])
  174. bottomAd = DictField(verbose_name = u"底部banner", default = {})
  175. pushBrokerUrl = StringField(verbose_name = u'经销商订阅的消息推送URL', default = settings.DEFAULT_DEALER_PUSH_BROKER_URL)
  176. limitDevNum = IntField(verbose_name = u'限制经销商最大设备数量', default = 9999)
  177. isPurePartner = BooleanField(verbose_name = u'是否为纯合伙人', default = False)
  178. userCount = IntField(verbose_name = u'总的用户数量', default = 0)
  179. moniAppCheckPointDict = DictField(verbose_name = u'是否需要弹出监督公众号的经销商检查点', default = DEFAULT_CHECKPOINT)
  180. forceFollowGzh = StringField(verbose_name=u'是否强制关注公众号', default='agent')#以代理商为准:'agent',强制关注:'yes',不允许强制关注:'no',‘永不关注’:‘never’,‘关不关注无所谓’:free
  181. devCount = IntField(verbose_name = u'设备数量,方便统计查询', default = 0)
  182. maxPackagePrice = MonetaryField(verbose_name = u"最大套餐价格", default = RMB(100.00))
  183. expressAddrList = EmbeddedDocumentListField(verbose_name = u'经销商的收货地址', document_type = DealerAddr)
  184. showServicePhone = BooleanField(verbose_name = u'是否允许使用服务电话', default = False)
  185. isShowBanner = BooleanField(verbose_name = u'是否显示代理商banner', default = True)
  186. hasTempPackage = BooleanField(verbose_name = u'是否启用临时套餐', default = False)
  187. freeModeDisplayedIdle = BooleanField(verbose_name = u'免费地址下端口状态显示为空闲', default = False)
  188. domain = StringField(verbose_name = u'api推送地址url', default = '')
  189. payAppType = StringField(verbose_name = u'商户类型', default = None)
  190. merchantApp = GenericLazyReferenceField(choices=MERCHANT_APP_LIST, verbose_name= u'商户', default=None)
  191. monitorPhone = StringField(verbose_name = u"提现审批员的手机号", default = "", max_length = 32)
  192. bankWithdrawFee = BooleanField(verbose_name = u"银行卡提现手续开关(资金池代理商开关和这个开关为双开关)", default = True)
  193. currencyCoins = BooleanField(verbose_name = u'金币是否通用(deprecated)', default = True)
  194. currencyMode = StringField(verbose_name = u'金币通用模式(allYes, allNo, asGroup)', default = None)
  195. # 有种情况下比如同一个手机号在两个代理商下申请的都有经销商 商户其实只用申请一个就行了
  196. bindMerSrc = StringField(verbose_name=u"共用的商户经销商ID")
  197. # 联动开关
  198. linkageSwitch = EmbeddedDocumentField(verbose_name=u"开关集合", document_type=DealerLinkageSwitch, default=DealerLinkageSwitch)
  199. apiInfo = LazyReferenceField(verbose_name=u'api应用信息', document_type=ApiAppInfo, default=None)
  200. disableAdPlan = LazyReferenceField(verbose_name=u'纯净版信息', document_type=DisableAdPlan, default=None)
  201. # 配置模板集合
  202. templateSet = EmbeddedDocumentField(verbose_name=u"开关集合", document_type=ConfigTemplate, default=ConfigTemplate)
  203. # 兼容老版本
  204. payAppJDAggre = LazyReferenceField(document_type=JDAggrePayApp, verbose_name=u'支付APP', default=None)
  205. payAppRCU = LazyReferenceField(document_type=RuralCreditUnionApp, verbose_name=u'河南农村信用社(仅用于支付)', default=None)
  206. meta = {
  207. 'indexes': [
  208. {
  209. 'fields': ['username', 'agentId'], 'unique': True
  210. },
  211. ],
  212. "collection": "Dealer",
  213. "db_alias": "default"
  214. }
  215. search_fields = ('username', 'nickname', 'remarks')
  216. def __str__(self):
  217. return '{}<id={} username={} nickname={} agentId={}>'.format(
  218. self.__class__.__name__, str(self.id), self.username, self.nickname, self.agentId)
  219. @property
  220. def currency_mode(self):
  221. if not self.currencyMode:
  222. if self.currencyCoins:
  223. return 'allYes'
  224. else:
  225. return 'allNo'
  226. else:
  227. return self.currencyMode
  228. @property
  229. def bossId(self):
  230. return self.id
  231. @property
  232. def myBoss(self):
  233. return self
  234. # 覆写. 需要对ruleDict参数进行一次转换
  235. def save(self, force_insert = False, validate = True, clean = True,
  236. write_concern = None, cascade = None, cascade_kwargs = None,
  237. _refs = None, save_condition = None, **kwargs):
  238. if 'defaultDiscountConfig' in kwargs:
  239. newRuleDict = {}
  240. for k, v in self.format_default_discount.items():
  241. newRuleDict[k.replace('.', '-')] = v
  242. self.defaultDiscountConfig = newRuleDict
  243. if "defaultCardDiscountConfig" in kwargs:
  244. newCardRuleDict = {}
  245. for k, v in self.format_card_discount.items():
  246. newCardRuleDict[k.replace('.', '-')] = v
  247. self.defaultCardDiscountConfig = newCardRuleDict
  248. return super(Dealer, self).save(**kwargs)
  249. def update(self, **kwargs):
  250. ruleDict = kwargs.pop('defaultDiscountConfig', None)
  251. if ruleDict is not None:
  252. newRuleDict = {}
  253. for k, v in ruleDict.items():
  254. newRuleDict[k.replace('.', '-')] = v
  255. kwargs.update({'defaultDiscountConfig': newRuleDict})
  256. cardRuleDict = kwargs.pop('defaultCardDiscountConfig', None)
  257. if cardRuleDict is not None:
  258. newCardRuleDict = {}
  259. for k, v in cardRuleDict.items():
  260. newCardRuleDict[k.replace('.', '-')] = v
  261. kwargs.update({'defaultCardDiscountConfig': newCardRuleDict})
  262. updated = super(Dealer, self).update(**kwargs)
  263. if not updated:
  264. raise UpdateError('failed to update dealer query kwargs(%s)' % (kwargs,))
  265. else:
  266. return updated
  267. @property
  268. def permissionList(self):
  269. return []
  270. @property
  271. def virtualCardOnlyCardSwitch(self):
  272. """
  273. 虚拟卡仅用于 实体卡的开关
  274. TODO zjl 这个是临时加的一个开关,目的在于不修改前台 达到 虚拟卡只用于实体卡而不能微信扫码使用的目的
  275. 等虚拟卡这一块儿整改的时候直接将这个去掉
  276. :return:
  277. """
  278. return "virtualCardOnlyCardSwitch" in self.features
  279. @property
  280. def switches(self):
  281. return {
  282. 'adShow': self.adShow,
  283. 'beforeCharge': self.beforeCharge,
  284. 'noRecharge': self.noRecharge,
  285. 'withdrawlNotify': self.withdrawlNotify,
  286. 'offlineNotify': self.offlineNotifySwitch,
  287. 'dailyIncomeReportPushSwitch': self.dailyIncomeReportPushSwitch,
  288. 'newUserPaymentOrderPushSwitch': self.newUserPaymentOrderPushSwitch,
  289. 'devFaultPushDealerSwitch': self.devFaultPushDealerSwitch,
  290. 'devFaultPushUserSwitch': self.devFaultPushUserSwitch,
  291. 'virtualCardOnlyCardSwitch': self.virtualCardOnlyCardSwitch
  292. }
  293. def query_feature_by_list(self, queryList):
  294. # type:(list)->dict
  295. dealerFeatures = self.feature_boolean_map
  296. resultFeatures = {}
  297. agent = Agent.objects.get(id=self.agentId) # type: Agent
  298. for query in queryList:
  299. if query in dealerFeatures:
  300. resultFeatures[query] = dealerFeatures[query]
  301. else:
  302. if query in agent.feature_boolean_map:
  303. resultFeatures[query] = agent.feature_boolean_map[query]
  304. else:
  305. resultFeatures[query] = False
  306. return resultFeatures
  307. def get_feature(self, feature_name):
  308. return self.query_feature_by_list([feature_name])
  309. def to_dict(self, shadow = False):
  310. rv = super(Dealer, self).to_dict(shadow = shadow)
  311. pointList = []
  312. for key, switch in self.moniAppCheckPointDict.items():
  313. try:
  314. point = MoniAppPoint.objects.get(key = key)
  315. except Exception as e:
  316. continue
  317. pointList.append({'name': point.name, 'key': key, 'switch': switch})
  318. rv.update({
  319. 'id': str(self.id),
  320. 'deviceBalance': self.sub_balance(DEALER_INCOME_TYPE.DEVICE_INCOME),
  321. 'adBalance': self.sub_balance(DEALER_INCOME_TYPE.AD_INCOME),
  322. "ledgerBalance": self.sub_balance(DEALER_INCOME_TYPE.LEDGER_CONSUME),
  323. 'balance': self.total_balance,
  324. 'wechat': self.wechat,
  325. 'description': self.description,
  326. 'managerialOpenId': self.managerialOpenId,
  327. 'managerialAppId': self.managerialAppId,
  328. 'agentId': self.agentId,
  329. 'bottomAd': self.bottomAd,
  330. 'featureList': self.feature_list,
  331. 'withdrawFeeRatio': self.withdrawFeeRatio,
  332. 'annualTrafficCost': self.annualTrafficCost,
  333. 'defaultDiscountConfig': self.defaultDiscountConfig,
  334. 'pushBrokerUrl': self.pushBrokerUrl,
  335. 'isPurePartner': self.isPurePartner,
  336. 'pointDict': pointList,
  337. 'forceFollowGzh': self.forceFollowGzh,
  338. 'smsVendor': self.smsVendor,
  339. 'hasTempPackage': self.hasTempPackage,
  340. 'freeModeDisplayedIdle': self.freeModeDisplayedIdle,
  341. 'offlineNotifySwitch': self.offlineNotifySwitch,
  342. 'offlineNotifyTime': self.offlineNotifyTime,
  343. 'currencyMode': self.currency_mode,
  344. 'payAppType': self.payAppType,
  345. 'supportedConsumptionShow': self.supportedConsumptionShow,
  346. })
  347. if hasattr(self, 'displayTempPackage'):
  348. rv.update({"displayTempPackage": self.displayTempPackage})
  349. rv.update(self.switches)
  350. return rv
  351. @classmethod
  352. def all(cls):
  353. return [dealer.to_dict() for dealer in cls.objects.all()]
  354. @staticmethod
  355. def cache_key(ownerId):
  356. # type:(Optional[ObjectId, str])->str
  357. return Dealer.CACHE_KEY.format(id = str(ownerId))
  358. @staticmethod
  359. def get_dealer(ownerId): # type:(ObjectId)->Optional[DealerDict, None]
  360. if not ownerId:
  361. return None
  362. dealer = cache.get(Dealer.cache_key(ownerId)) # type: dict
  363. if dealer:
  364. return DealerDict(dealer)
  365. else:
  366. dealer = Dealer.objects(id = ownerId).first() # type: Dealer
  367. if dealer:
  368. value = dealer.to_dict()
  369. cache.set(Dealer.cache_key(ownerId), value)
  370. return DealerDict(value)
  371. else:
  372. logger.error('can not find dealer from db,id=%s' % ownerId)
  373. return None
  374. @staticmethod
  375. def invalid_cache(ownerId):
  376. # type:(Optional[ObjectId, str])->None
  377. cache.delete(Dealer.cache_key(ownerId))
  378. @staticmethod
  379. def update_dealer(ownerId, **valueDict):
  380. # type:(ObjectId, **Any)->bool
  381. try:
  382. if 'noRecharge' in valueDict and valueDict['noRecharge']:
  383. valueDict.update({'beforeCharge': False})
  384. elif 'beforeCharge' in valueDict and valueDict['beforeCharge']:
  385. valueDict.update({'noRecharge': False})
  386. Dealer.objects(id = ObjectId(ownerId)).update(**valueDict)
  387. except Exception as e:
  388. logger.exception('update dealer info error=%s' % e)
  389. return False
  390. Dealer.invalid_cache(ownerId)
  391. return True
  392. @property
  393. def isManagerialOpenIdBound(self):
  394. return self.managerialOpenId != ''
  395. def get_bound_pay_openid(self, key):
  396. # type: (str)->str
  397. pay_openid_map = self.payOpenIdMap # type: dict
  398. bound = pay_openid_map.get(key, BoundOpenInfo(openId = '')) # type: BoundOpenInfo
  399. return bound.openId
  400. def set_bound_pay_openid(self, key, **payload):
  401. # type: (str, Dict)->None
  402. self.payOpenIdMap[key] = BoundOpenInfo(**payload)
  403. def get_bound_pay_info(self, key):
  404. pay_openid_map = self.payOpenIdMap
  405. bound = pay_openid_map.get(key, BoundOpenInfo(openId = ''))
  406. return bound.to_detail_dict()
  407. def set_bound_pay_info(self, key, **payload):
  408. bound = BoundOpenInfo(**payload)
  409. self.payOpenIdMap[key] = bound
  410. return
  411. def isAutoWithdrawOpenIdBound(self, key):
  412. pay_openid_map = self.payOpenIdMap # type: dict
  413. bound = pay_openid_map.get(key, BoundOpenInfo(openId = ''))
  414. return bound.openId != "" and bound.nickname != ""
  415. @property
  416. def format_default_discount(self):
  417. tmp_rule_dict = {}
  418. for k, v in self.defaultDiscountConfig.items():
  419. tmp_rule_dict[k.replace('-', '.')] = v
  420. return tmp_rule_dict
  421. @property
  422. def format_card_discount(self):
  423. card_rule_dict = dict()
  424. for k, v in self.defaultCardDiscountConfig.items():
  425. card_rule_dict[k.replace("-", ".")] = v
  426. return card_rule_dict
  427. @staticmethod
  428. def get_dealers(agentId):
  429. dealers = Dealer.get_collection().find({'agentId': agentId})
  430. return [str(dealer['_id']) for dealer in dealers]
  431. @staticmethod
  432. def get_dealer_from_groupId(groupId):
  433. group = Group.get_group(groupId)
  434. if group is not None:
  435. dealer = Dealer.objects(id = group['ownerId']).first()
  436. else:
  437. group = Group.objects(id = groupId).first()
  438. dealer = Dealer.objects(id = group.ownerId).first()
  439. return dealer
  440. def get_own_devices(self):
  441. groupIds = Group.get_group_ids_of_dealer(str(self.id))
  442. devNoList = Device.get_devNos_by_group(groupIds)
  443. return Device.get_dev_by_nos(devNoList).values()
  444. @property
  445. def income_aggregate_source(self):
  446. # type: ()->Dict[str, AnyStr]
  447. """
  448. e.g. {'ad': u'广告'}
  449. :return:
  450. """
  451. supports = [item for item in self.supportedIncomeAggregate if item != 'ad']
  452. g = itemgetter(*supports)
  453. return dict(zip(supports, g(DEALER_INCOME_SOURCE_TRANSLATION)))
  454. @property
  455. def consumption_aggregate_source(self):
  456. # type: ()->Dict[str, AnyStr]
  457. """
  458. e.g. {'elect': u'电量'}
  459. :return:
  460. """
  461. g = itemgetter(*self.supportedConsumptionAggregate)
  462. return dict(zip(self.supportedConsumptionAggregate, g(DEALER_CONSUMPTION_AGG_KIND_TRANSLATION)))
  463. def set_agent_profit_share(self, share):
  464. # type: (Percent)->int
  465. """
  466. :param share:
  467. :return:
  468. """
  469. updated = self.update(agentProfitShare = share.mongo_amount)
  470. return updated
  471. def set_agent_mer_profit_share(self, share):
  472. # type: (Percent)->int
  473. """
  474. :param share:
  475. :return:
  476. """
  477. updated = self.update(agentMerProfitShare = share.mongo_amount)
  478. return updated
  479. @classmethod
  480. def set_traffic_costs_multiply(cls, dealer_ids, cost):
  481. # type: (List[ObjectId], RMB)->int
  482. updated = cls.get_collection().update_many({'_id': {'$in': dealer_ids}},
  483. {'$set': {'annualTrafficCost': cost.mongo_amount}},
  484. upsert = False)
  485. return updated
  486. @property
  487. def login_url(self):
  488. # type:()->str
  489. return concat_dealer_access_entry_url(agentId = self.agentId)
  490. @property
  491. def withdraw_sms_provider(self):
  492. if self.monitorPhone:
  493. return dealerMonitorWithdrawSMSProvider
  494. else:
  495. return dealerWithdrawSMSProvider
  496. @property
  497. def withdraw_sms_phone_number(self):
  498. if self.monitorPhone:
  499. return str(self.monitorPhone)
  500. else:
  501. return str(self.username)
  502. def withdraw_bank_card(self, bank_card_no = None):
  503. if not bank_card_no:
  504. merchant = Merchant.objects(ownerId = str(self.id)).first()
  505. else:
  506. merchant = Merchant.objects(accountCode = bank_card_no).first()
  507. if not merchant or not merchant.accountCode:
  508. return None
  509. else:
  510. return BankCard(
  511. id = merchant.id,
  512. bankName = merchant.parentBankName,
  513. branchName = merchant.subBankName,
  514. holderName = merchant.merchantName,
  515. cardNo = merchant.accountCode,
  516. cardType = merchant.accountType,
  517. province = merchant.bankProvinceName,
  518. city = merchant.bankCityName,
  519. accountType = merchant.accountType,
  520. cnapsCode = merchant.cnapsCode,
  521. manual = merchant.manual
  522. )
  523. @property
  524. def auto_withdraw_bound_open_id(self):
  525. from apps.web.helpers import get_wechat_auth_bridge
  526. auth_bridge = get_wechat_auth_bridge(source = self, app_type = APP_TYPE.WECHAT_WITHDRAW)
  527. return self.get_bound_pay_openid(auth_bridge.bound_openid_key)
  528. def record_income(self, source, source_key, money):
  529. # type: (str, str, RMB)->bool
  530. assert isinstance(money, RMB), 'money has to be a RMB instance'
  531. assert source in DEALER_INCOME_SOURCE.choices(), 'not support this source'
  532. assert source_key, 'source key must not be none'
  533. income_type = DealerConst.MAP_SOURCE_TO_TYPE[source]
  534. return self.incr_fund(income_type, source_key, money)
  535. def new_withdraw_handler(self, record):
  536. # type: (WithdrawRecord) -> WithdrawHandler
  537. from apps.web.dealer.withdraw import DealerWithdrawHandler
  538. return DealerWithdrawHandler(self, record)
  539. def new_withdraw_record(self, withdraw_gateway, pay_entity, source_key, income_type, amount, pay_type, manual, recurrent):
  540. """
  541. 创建自动提现的单子
  542. :param withdraw_gateway: 提现网关 WechatPaymentGateway
  543. :param pay_entity: 银行卡 BankCard
  544. :param income_type: 收益类型 str
  545. :param amount: 提现金额(非实际到账) RMB
  546. :param pay_type: 提现的方式 str
  547. :param manual: 手动提现 Bool
  548. :param recurrent: 是否首次 Bool
  549. :return: WithdrawRecord
  550. """
  551. agent = Agent.objects(id = self.agentId).first() # type: Agent
  552. if not agent:
  553. logger.error('agent is None, agentId={}'.format(self.agentId))
  554. raise ServiceException(
  555. {
  556. 'result': 0,
  557. 'description': u'系统繁忙,请稍后再试',
  558. 'payload': {}
  559. })
  560. agentFeeRation = agent.withdrawFeeRatio
  561. managerFeeRatio = agent.withdrawFeeRatioCost
  562. dealerFeeRatio = self.withdrawFeeRatio
  563. # 微信或者支付宝平台服务费
  564. serviceFee = amount * (dealerFeeRatio.as_ratio)
  565. # 计算微信转银行卡手续费
  566. has_bank_fee = False
  567. if pay_type == WITHDRAW_PAY_TYPE.BANK:
  568. if recurrent:
  569. has_bank_fee = self.auto_withdraw_bank_fee
  570. else:
  571. withdraw_agent = withdraw_gateway.occupant # type: Agent
  572. has_bank_fee = withdraw_agent.dealerBankWithdrawFee and self.bankWithdrawFee
  573. if has_bank_fee:
  574. bank_trans_fee = min(RMB('25.00'), max(RMB('0.10'), amount * Permillage('1').as_ratio))
  575. else:
  576. bank_trans_fee = RMB('0.00')
  577. actualPay = amount - serviceFee - bank_trans_fee
  578. if actualPay < RMB(settings.WITHDRAW_MINIMUM):
  579. logger.error('amount is not enough.')
  580. raise ServiceException(
  581. {
  582. 'result': 0,
  583. 'description': u"提现实际到账金额不能少于{}元".format(RMB(settings.WITHDRAW_MINIMUM)),
  584. 'payload': {}
  585. }
  586. )
  587. if dealerFeeRatio > managerFeeRatio:
  588. earned = amount * ((dealerFeeRatio - managerFeeRatio).as_ratio)
  589. if earned < RMB(0):
  590. earned = RMB(0)
  591. elif earned > serviceFee:
  592. earned = serviceFee
  593. # 分给代理商
  594. agentEarned = RMB(0)
  595. if dealerFeeRatio > agentFeeRation:
  596. agentEarned = amount * ((dealerFeeRatio - agentFeeRation).as_ratio)
  597. if agentEarned < RMB(0):
  598. agent_earned = RMB(0)
  599. elif agentEarned > earned:
  600. agentEarned = earned
  601. # 代理商分完如果还有 继续分给厂商
  602. managerEarned = (earned - agentEarned)
  603. if managerEarned < RMB(0):
  604. managerEarned = RMB(0)
  605. else:
  606. agentEarned = RMB(0)
  607. managerEarned = RMB(0)
  608. partition = [
  609. {
  610. 'role': ROLE.agent,
  611. 'id': self.agentId,
  612. 'cost': agentFeeRation.mongo_amount,
  613. 'earned': agentEarned.mongo_amount
  614. },
  615. {
  616. 'role': ROLE.manager,
  617. 'id': str(agent.primary_agent.id),
  618. 'cost': managerFeeRatio.mongo_amount,
  619. 'earned': managerEarned.mongo_amount
  620. }
  621. ]
  622. return WithdrawRecord.create(
  623. self,
  624. withdraw_gateway = withdraw_gateway,
  625. pay_entity = pay_entity,
  626. source_key = source_key,
  627. income_type = income_type,
  628. pay_type = pay_type,
  629. fund_map = {
  630. 'amount': amount,
  631. 'serviceFee': serviceFee,
  632. 'bankTransFee': bank_trans_fee,
  633. 'actualPay': actualPay,
  634. 'withdrawFeeRatio': dealerFeeRatio,
  635. 'partition': partition
  636. },
  637. manual = manual,
  638. recurrent = recurrent
  639. )
  640. def get_addr_list(self):
  641. result = []
  642. for obj in self.expressAddrList:
  643. if not obj.default:
  644. result.append({'tel':obj.tel,'name':obj.name,'addr':obj.addr,'id':str(obj.id),'default':obj.default})
  645. else:
  646. result.insert(0, {'tel':obj.tel,'name':obj.name,'addr':obj.addr,'id':str(obj.id),'default':obj.default})
  647. return result
  648. @classmethod
  649. def get_auto_withdraw_dealers(cls):
  650. dealers = list()
  651. for _dealer in cls.objects.filter(withdrawOptions__autoWithdrawSwitch = True): # type: Dealer
  652. if "autoWithdraw" in _dealer.features:
  653. dealers.append(_dealer)
  654. return dealers
  655. @classmethod
  656. def get_income_balance_list(cls, dealer, incomeType, minNum=0):
  657. """
  658. 计算经销商在 每个收益来源下面的可提现余额 以及 手续费
  659. :param dealer:
  660. :param incomeType:
  661. :param minNum: 过滤金额
  662. :return: list --> (sourceKey, amount) 来源 当前账户余额
  663. """
  664. assert incomeType in DEALER_INCOME_TYPE.choices(), 'not support this income type'
  665. balanceDict = getattr(dealer, DealerConst.MAP_TYPE_TO_FIELD[incomeType])
  666. withdrawInfoList = list()
  667. for sourceKey, item in balanceDict.items():
  668. if not WithdrawGateway.is_ledger(sourceKey):
  669. continue
  670. balance = item.balance
  671. if balance <= RMB(0):
  672. continue
  673. if balance < RMB(minNum):
  674. continue
  675. withdrawInfoList.append((sourceKey, balance))
  676. return withdrawInfoList
  677. @classmethod
  678. def factory(cls, **kwargs):
  679. factory_type = kwargs.pop('factory_type')
  680. if factory_type == 'app':
  681. app_type = kwargs.pop('app_type')
  682. return lambda dealer: cls.from_dealer(dealer = dealer, app_type = app_type, **kwargs)
  683. else:
  684. raise InvalidParameter(u'参数错误')
  685. @classmethod
  686. def from_dealer(cls, dealer, app_type, **kwargs):
  687. appKey = "{}_app".format(app_type)
  688. attr_or_func = getattr(dealer, appKey)
  689. if callable(attr_or_func):
  690. return attr_or_func(**kwargs)
  691. else:
  692. return attr_or_func
  693. def _check_merchant_valid(self, app):
  694. if isinstance(app, JDAggrePayApp):
  695. if self.payAppType != PayAppType.JD_AGGR:
  696. return False
  697. else:
  698. return True
  699. elif isinstance(app, JDOpenPayApp):
  700. if self.payAppType != PayAppType.JD_OPEN:
  701. return False
  702. else:
  703. return True
  704. elif isinstance(app, RuralCreditUnionApp):
  705. if self.payAppType != PayAppType.RCU:
  706. return False
  707. else:
  708. return True
  709. else:
  710. return False
  711. @property
  712. def active_pay_app_field(self):
  713. if not self.payAppType:
  714. return None
  715. if self.merchantApp:
  716. return 'merchantApp'
  717. else:
  718. if self.payAppType == PayAppType.JD_AGGR:
  719. return 'payAppJDAggre'
  720. elif self.payAppType == PayAppType.RCU:
  721. return 'payAppRCU'
  722. else:
  723. return None
  724. @property
  725. def customizedMerchantApp(self): # type: ()->Optional[PayAppT]
  726. if not self.payByMerchant:
  727. return None
  728. app_ref = getattr(self, self.active_pay_app_field, None)
  729. if not app_ref:
  730. raise Exception(u'商户配置错误(1001)')
  731. my_app = app_ref.fetch()
  732. if not my_app.valid:
  733. raise Exception(u'商户配置错误(1002)')
  734. if not self._check_merchant_valid(my_app):
  735. raise Exception(u'商户配置错误(1003)')
  736. my_app.occupantId = str(self.id)
  737. my_app.occupant = self
  738. return my_app
  739. @property
  740. def partnerMerchantApp(self):
  741. # TODO: 先简单修改,后面整改
  742. if self.merchantApp:
  743. app = self.merchantApp
  744. elif self.payAppJDAggre:
  745. app = self.payAppJDAggre
  746. else:
  747. return None
  748. my_app = app.fetch()
  749. if not my_app.valid:
  750. raise Exception(u'商户配置错误(1002)')
  751. my_app.occupantId = str(self.id)
  752. my_app.occupant = self
  753. return my_app
  754. @property
  755. def payByMerchant(self):
  756. return self.payAppType
  757. def wechat_env_pay_app(self, role = None, pay_app_type = None):
  758. """
  759. 对于经销商而言 不存在一级经销商和二级经销商的区别 不需要再次递归寻找
  760. :param role:
  761. :param pay_app_type:
  762. :return:
  763. """
  764. return self.customizedMerchantApp
  765. def alipay_env_pay_app(self, role = None, pay_app_type = None):
  766. return self.customizedMerchantApp
  767. def jd_env_pay_app(self, role = None, pay_app_type = None):
  768. app = self.customizedMerchantApp
  769. if isinstance(app, JDAggrePayApp) or isinstance(app, JDOpenPayApp):
  770. return app
  771. else:
  772. raise Exception(u'系统配置错误(第三方支付)')
  773. def swap_env_pay_app(self):
  774. my_app = SwapPayApp()
  775. my_app.occupantId = str(self.id)
  776. my_app.occupant = self
  777. return my_app
  778. @property
  779. def my_avatar(self):
  780. if self.avatar:
  781. return self.avatar
  782. if self.isManagerialOpenIdBound and self.wechatAuthUserInfo:
  783. return self.wechatAuthUserInfo.get('avatar', '')
  784. return ''
  785. def withdraw_source_key(self, pay_app = None):
  786. # type:(PayAppBase)->str
  787. """
  788. 经销商提现资金池KEY是虚拟的, 仅仅对应账号的资金统计值
  789. :param pay_app:
  790. :return:
  791. """
  792. return APP_KEY_DELIMITER.join(
  793. [WithdrawGateway.NO_LEDGER_PREFIX, pay_app.pay_app_type, getattr(pay_app, '__gateway_key__')])
  794. def handle_with_partners(self, partners):
  795. # type:(list) -> list
  796. """
  797. 处理 合伙人 完成一些参数校验
  798. :param partners:
  799. :return:
  800. """
  801. if not partners:
  802. return list()
  803. new_partners = list(partners)
  804. if self.payByMerchant:
  805. return self.handle_with_merchant_partner(new_partners)
  806. else:
  807. return self.handle_with_normal_partner(new_partners)
  808. def handle_with_merchant_partner(self, partners):
  809. # type:(list) -> list
  810. """
  811. 处理 合伙人 完成一些参数校验 不存在的情况下不要新建
  812. :param partners:
  813. :return:
  814. """
  815. mobileMap = {_partner['tel']: _partner for _partner in partners}
  816. newPartners = list()
  817. dealers = Dealer.objects.filter(username__in=mobileMap.keys(), agentId=self.agentId)
  818. # 总人数鉴定
  819. if len(dealers) != len(partners):
  820. notRegisterDealers = set(mobileMap.keys()) - set([_.username for _ in dealers])
  821. raise ServiceException({"result": 0, "description": u"合伙人尚未注册,请在注册合伙人 {} 为经销商!".format(notRegisterDealers)})
  822. # 同一个代理商下 经销商username唯一
  823. for _dealer in dealers:
  824. if _dealer == self:
  825. raise ServiceException({"result": 0, "description": u'合伙人<{}>不能是经销商本人'.format(_dealer.username)})
  826. # 分成不为0的商户必须 配有京东APP收款
  827. if not _dealer.payAppJDAggre and int(mobileMap[_dealer.username]["percent"]) > 0:
  828. raise ServiceException({"result": 0, "description": u'合伙人<{}>必须开通商户号'.format(_dealer.username)})
  829. # 将ID添加进去
  830. _partner = mobileMap.get(str(_dealer.username))
  831. _partner.update({"id": str(_dealer.id)})
  832. newPartners.append(_partner.copy())
  833. # TODO 合伙人添加的时候 应该是只是添加 而并不是有权限去修改诸如昵称一类的信息
  834. return newPartners
  835. def handle_with_normal_partner(self, partners):
  836. # type:(list) -> list
  837. """
  838. 处理 合伙人 完成一些参数校验
  839. 对于普通的合伙人校验逻辑还是和之前的一样 有则更新 没有创建
  840. :param partners:
  841. :return:
  842. """
  843. from apps.web.dealer.utils import create_partner, update_partner
  844. mobileList = [_partner['tel'] for _partner in partners]
  845. dealers = Dealer.objects.filter(username__in = mobileList, agentId = self.agentId)
  846. partnerDict = {}
  847. for dealer in dealers:
  848. if dealer == self:
  849. raise ServiceException({"result": 0, "description": u'合伙人<{}>不能是经销商本人'.format(dealer.username)})
  850. partnerDict[dealer.username] = dealer
  851. for partner in partners:
  852. if partner['tel'] not in partnerDict:
  853. newDealer = create_partner(
  854. username = partner['tel'],
  855. password = md5("1234567"),
  856. agentId = self.agentId,
  857. nickname = partner['name'],
  858. annualTrafficCost = self.annualTrafficCost,
  859. agentProfitShare = self.agentProfitShare
  860. )
  861. partner['id'] = str(newDealer.id)
  862. else:
  863. dealerId = str(partnerDict[partner['tel']].id)
  864. updated = update_partner(
  865. dealerId = ObjectId(dealerId),
  866. agentId = self.agentId,
  867. nickname = partner['name'],
  868. agentProfitShare = self.agentProfitShare
  869. )
  870. if not updated:
  871. raise ServiceException({"result": 0, "description": u"合伙人(%s)信息更新失败" % partnerDict[partner['tel']]})
  872. partner['id'] = dealerId
  873. partner['percent'] = str(partner['percent'])
  874. return partners
  875. @classmethod
  876. def get_cooperative_dealer_ids(cls, dealerId):
  877. rcds = Group.get_collection().find({'partnerList.id': dealerId})
  878. dealer_ids = set([dealerId])
  879. for rcd in rcds: # type: Group
  880. dealer_ids.add(rcd['ownerId'])
  881. return list(dealer_ids)
  882. @classmethod
  883. def get_cooperative_group_ids(cls, dealerId):
  884. gs = Group.get_collection().find({'partnerList.id': dealerId})
  885. groupIds = []
  886. for _ in gs:
  887. groupIds.append(str(_['_id']))
  888. gsMe = Group.get_collection().find({'ownerId': dealerId})
  889. for _ in gsMe:
  890. groupIds.append(str(_['_id']))
  891. return groupIds
  892. @property
  893. def is_inhouse_wallet(self):
  894. if self.payByMerchant: # 经销商配置了自己商户号的,不能自动充值
  895. return False
  896. app = get_app(source = self, app_type = APP_TYPE.WECHAT_ENV_PAY, role = ROLE.myuser)
  897. return getattr(app, 'inhouse', False)
  898. @property
  899. def defaultMonthlyPackage(self):
  900. """
  901. 默认的包月套餐的列表
  902. :return:
  903. """
  904. return MonthlyPackageTemp.default_one(ownerId=str(self.id))
  905. @property
  906. def monthlyPackage(self):
  907. """
  908. 所有的包月套餐的列表
  909. :return:
  910. """
  911. return MonthlyPackageTemp.get_template_by_dealer(str(self.id))
  912. def isJosEnable(self, clientEnv="wechat"):
  913. """
  914. 判断该经销商是否能够使用京东的拉新活动
  915. # 由于分账的原因 目前只能够走 使用我们自身资金池的用户
  916. :param clientEnv: 客户端的环境
  917. :return:
  918. """
  919. if self.payByMerchant:
  920. # 开启商户的经销商 不能使用
  921. return False
  922. from apps.web.helpers import get_app
  923. if clientEnv == "wechat":
  924. app_type = APP_TYPE.WECHAT_ENV_PAY
  925. elif clientEnv == "jd":
  926. app_type = APP_TYPE.JD_ENV_PAY
  927. else:
  928. return None
  929. app = get_app(source=self, app_type=app_type, role=ROLE.myuser)
  930. # 首先判断是否是 我们自己的资金池 判断方式为 app的agent为平台agent
  931. # 再其次判断是否是京东支付 否则用户记账会记在不通的平台上 容易被经销商发现
  932. if app.occupantId == settings.MY_PRIMARY_AGENT_ID and app.pay_app_type == PayAppType.JD_AGGR:
  933. return app
  934. # 不满足条件的 都不允许使用
  935. return None
  936. def limit_filter_date(self, startTime, endTime):
  937. # type:(str, str)->(str, str)
  938. if not startTime:
  939. startTime = datetime.datetime.now().strftime('%Y-%m-%d')
  940. if not endTime:
  941. endTime = datetime.datetime.now().strftime('%Y-%m-%d')
  942. if str(self.id) == '5d132407003048494763a51b':
  943. # 港德的隐藏数据需求
  944. if startTime < '2021-01-01':
  945. startTime = '2021-01-01'
  946. if endTime < '2021-01-01':
  947. startTime = '2021-01-01'
  948. endTime = '2020-12-31'
  949. else:
  950. if startTime > endTime:
  951. endTime = startTime
  952. return startTime, endTime
  953. @classmethod
  954. def limit_filter_year(cls, dealerId, year):
  955. if dealerId == '5d132407003048494763a51b':
  956. # 港德的隐藏数据需求
  957. if int(year) < 2021:
  958. return None
  959. return year
  960. def get_currency_group_ids(self, group, groups = None):
  961. # type: (Optional[Group, GroupDict], List[Optional[Group, GroupDict]])->list
  962. dealer_dict = DealerDict(self.to_dict()) # type: DealerDict
  963. return dealer_dict.get_currency_group_ids(group, groups)
  964. @property
  965. def ad_show(self):
  966. if self.adShow:
  967. return 'show'
  968. else:
  969. return self.noAdPolicy
  970. @property
  971. def minProfitShare(self):
  972. """
  973. 经销商 所有的地址中 经销商的最低的分成比例
  974. :return:
  975. """
  976. profitShare = Percent('100')
  977. for _group in Group.objects.filter(ownerId=str(self.id)):
  978. if not _group.partnerList:
  979. continue
  980. for _partner in _group.partnerList:
  981. _profitShare = Percent(_partner["percent"])
  982. if _profitShare < profitShare:
  983. profitShare = _profitShare
  984. return profitShare
  985. def rent_freeze_balance(self, income_type, money, source_key, transaction_id): # type:(str, RMB, str, str)->bool
  986. """
  987. 出租设备的冻结订单
  988. :param income_type:
  989. :param money:
  990. :param source_key:
  991. :param transaction_id:
  992. :return:
  993. """
  994. assert source_key, 'gateway is null'
  995. assert transaction_id, 'transaction id is null'
  996. orderType = "inHandRentOrderList"
  997. field = self.income_field_name(income_type = income_type)
  998. fund_key = self.fund_key(income_type = income_type, source_key = source_key)
  999. query = {'_id': self.id, '{}.transaction_id'.format(orderType): {'$ne': transaction_id}}
  1000. update = {
  1001. '$inc': {
  1002. '{fund_key}.balance'.format(fund_key = fund_key): (-money).mongo_amount
  1003. },
  1004. '$addToSet': {
  1005. '{}'.format(orderType): {
  1006. 'transaction_id': transaction_id,
  1007. 'field': field,
  1008. 'key': source_key,
  1009. 'value': money.mongo_amount
  1010. }
  1011. }
  1012. }
  1013. result = self.get_collection().update_one(query, update, upsert = False)
  1014. return bool(result.modified_count == 1)
  1015. def rent_clear_frozen_balance(self, transaction_id): # type:(str)->bool
  1016. """
  1017. 订单扣款完毕 清除冻结信息
  1018. :param transaction_id:
  1019. :return:
  1020. """
  1021. assert transaction_id, 'transaction id is null'
  1022. orderType = "inHandRentOrderList"
  1023. query = {'_id': self.id, '{}.transaction_id'.format(orderType): transaction_id}
  1024. update = {
  1025. '$pull': {
  1026. '{}'.format(orderType):
  1027. {
  1028. 'transaction_id': transaction_id
  1029. }
  1030. }
  1031. }
  1032. result = self.get_collection().update_one(query, update, upsert = False)
  1033. return bool(result.modified_count == 1)
  1034. def rent_recover_frozen_balance(self, income_type, money, source_key, transaction_id): # type:(str, RMB, str, str)->bool
  1035. """
  1036. 回滚冻结的支付订单余额
  1037. :param source_key:
  1038. :param income_type:
  1039. :param money:
  1040. :param transaction_id:
  1041. :return:
  1042. """
  1043. assert source_key, 'gateway is null'
  1044. assert transaction_id, 'transaction id is null'
  1045. orderType = "inHandRentOrderList"
  1046. fund_key = self.fund_key(income_type = income_type, source_key = source_key)
  1047. query = {'_id': self.id, '{}.transaction_id'.format(orderType): transaction_id}
  1048. update = {
  1049. '$inc': {
  1050. '{fund_key}.balance'.format(fund_key = fund_key): money.mongo_amount
  1051. },
  1052. '$pull': {
  1053. '{}'.format(orderType):
  1054. {
  1055. 'transaction_id': transaction_id
  1056. }
  1057. }
  1058. }
  1059. result = self.get_collection().update_one(query, update, upsert = False)
  1060. return bool(result.modified_count == 1)
  1061. def get_merchant_split_id(self):
  1062. """
  1063. 获取商户号绑定的email
  1064. :return:
  1065. """
  1066. app = self.partnerMerchantApp # type: PayAppBase
  1067. if not app:
  1068. return None
  1069. else:
  1070. return app.split_id
  1071. def check_merchant_conditions(self): # type:() -> None
  1072. """
  1073. 检查商户是否准备就绪,商户开通前的最后一步检查
  1074. 经销商需要检查 所有的合伙人是否完成开通
  1075. """
  1076. # 先检查自己
  1077. if not self.merchantApp and not self.payAppJDAggre:
  1078. raise MerchantError(u"商户尚未绑定,开通失败")
  1079. # 然后检查合伙人
  1080. partnerIds = Group.get_lawful_partner_ids(str(self.id))
  1081. no_merchant_list = []
  1082. for _id in partnerIds:
  1083. _dealer = Dealer.objects(id = _id).first() # type: Dealer
  1084. # 查询是否 已经绑定 京东商户app
  1085. try:
  1086. app = _dealer.customizedMerchantApp
  1087. except Exception as e:
  1088. logger.exception(e)
  1089. app = None
  1090. if not isinstance(app, JDAggrePayApp):
  1091. no_merchant_list.append("{} {}".format(_dealer.nickname, _dealer.username))
  1092. # 检查是否有状态不成功的
  1093. if no_merchant_list:
  1094. raise MerchantError(u"当前账户尚有未绑定银行卡的合伙人,请联系相应人员进行银行卡绑定,否则无法对账户进行分账\n{}".format("\n".join(no_merchant_list)))
  1095. def confirm_merchant(self, source): # type: (Optional[MerchantSourceInfo, JDOpenApplyInfo]) -> Dealer
  1096. """ 商户已经是确认状态所需要处理的事情 """
  1097. if not source.support_jdaggre():
  1098. return
  1099. if not source.owner == self:
  1100. return
  1101. # 首先获取自己的商户
  1102. app = source.create_app()
  1103. self.merchantApp = app
  1104. return self.save()
  1105. def success_merchant(self):
  1106. """
  1107. 确认使用商户的动作
  1108. """
  1109. self.payAppType = PayAppType.JD_AGGR
  1110. return self.save()
  1111. @property
  1112. def merchantRegister(self):
  1113. """
  1114. 使用的京东商户 实际注册的人
  1115. 一个经销商有两个账号
  1116. """
  1117. if not self.bindMerSrc:
  1118. logger.error("[merchantRegister] self = {}".format(self.id))
  1119. return self
  1120. return self.__class__.objects.get(id=self.bindMerSrc)
  1121. def get_linkage_switch(self):
  1122. """
  1123. 获取经销商的两级联动开关设置
  1124. """
  1125. return {
  1126. LinkageSwitchEnum.CHARGE_INSURANCE: self.linkageSwitch.chargeInsurance
  1127. }
  1128. def turn_on(self, category):
  1129. """
  1130. 打开保险的开关
  1131. """
  1132. # 打开保险前的前置检查
  1133. if category == LinkageSwitchEnum.CHARGE_INSURANCE:
  1134. if not self.is_inhouse_wallet:
  1135. return False
  1136. self.linkageSwitch.chargeInsurance = True
  1137. return bool(self.update(linkageSwitch__chargeInsurance=self.linkageSwitch.chargeInsurance))
  1138. def turn_off(self, category):
  1139. """
  1140. 关掉保险的开关
  1141. """
  1142. if category == LinkageSwitchEnum.CHARGE_INSURANCE:
  1143. self.linkageSwitch.chargeInsurance = False
  1144. return bool(self.update(linkageSwitch__chargeInsurance=self.linkageSwitch.chargeInsurance))
  1145. def get_all_api_devs(self):
  1146. devList = Device.get_devs_by_ownerId(str(self.id))
  1147. return filter(lambda _: _.isApi == True, devList)
  1148. @property
  1149. def api_quota_info(self):
  1150. totalQuota = self.api_app.apiDeviceMax
  1151. devs = self.get_all_api_devs()
  1152. usedQuota = len(devs)
  1153. remainingQuota = totalQuota - usedQuota if totalQuota - usedQuota > 0 else 0
  1154. return {
  1155. 'totalQuota': totalQuota,
  1156. 'usedQuota': usedQuota,
  1157. 'remainingQuota': remainingQuota
  1158. }
  1159. @property
  1160. def api_app(self):
  1161. if self.apiInfo:
  1162. return self.apiInfo.fetch()
  1163. else:
  1164. return ApiAppInfo()
  1165. def update_api_app(self, **payload):
  1166. if not self.apiInfo:
  1167. self.apiInfo = ApiAppInfo().save()
  1168. self.save()
  1169. apiInfo = self.apiInfo.fetch() # type: ApiAppInfo
  1170. apiInfo.update(**payload)
  1171. @property
  1172. def disable_ad_plan(self):
  1173. if self.disableAdPlan: # type: DisableAdPlan
  1174. return self.disableAdPlan.fetch()
  1175. else:
  1176. return DisableAdPlan()
  1177. def update_disable_ad_plan(self, **payload):
  1178. if not self.disableAdPlan:
  1179. self.disableAdPlan = DisableAdPlan().save()
  1180. self.save()
  1181. disable_ad_plan = self.disableAdPlan.fetch() # type: DisableAdPlan
  1182. disable_ad_plan.update(**payload)
  1183. def disable_ad_quota_info(self):
  1184. all_devs = Device.get_devs_by_ownerId(str(self.id))
  1185. now = datetime.datetime.now()
  1186. configured_filter = lambda _: _.disableADExpireDate and _.disableADExpireDate > now
  1187. expiring_soon_filter = lambda _: _.disableADExpireDate and (_.disableADExpireDate - now).total_seconds() <= 60 * 60 * 24 * 15
  1188. configured = len(filter(configured_filter, all_devs))
  1189. expiringSoon= len(filter(expiring_soon_filter, all_devs))
  1190. notConfigured = len(Device.get_devs_by_ownerId(str(self.id))) - configured
  1191. return {
  1192. 'configured': configured,
  1193. 'expiringSoon': expiringSoon,
  1194. 'notConfigured': notConfigured
  1195. }
  1196. def query_home_page_layout(self):
  1197. menuDict = {}
  1198. menu_features = []
  1199. for menu, default_value in Const.MAIN_MENU_LIST.iteritems():
  1200. if default_value:
  1201. menu_features.append('hide_{}'.format(menu))
  1202. else:
  1203. menu_features.append(menu)
  1204. menuDict[menu] = default_value
  1205. homeDataDict = {}
  1206. home_data_features = ['show_ad_income', 'show_offline_coins']
  1207. for menu, default_value in Const.HOME_PAGE_DATA_LIST.iteritems():
  1208. homeDataDict[menu] = default_value
  1209. queryResult = self.query_feature_by_list(menu_features + home_data_features)
  1210. for k, v in queryResult.items():
  1211. if k in home_data_features:
  1212. if k == 'show_ad_income':
  1213. homeDataDict['today_ad_income'] = v
  1214. elif k == 'show_offline_coins':
  1215. homeDataDict['offline_coins'] = v
  1216. else:
  1217. if 'hide_' in k:
  1218. if v:
  1219. menu_name = k.replace('hide_', '')
  1220. menuDict[menu_name] = False
  1221. else:
  1222. menu_name = k
  1223. menuDict[menu_name] = v
  1224. return menuDict, homeDataDict
  1225. @property
  1226. def service_phone(self):
  1227. # 如果代理商接管客服电话,就显示代理商的服务电话
  1228. if self.linkageSwitch.agentProxyServicePhone is True:
  1229. agent = Agent.objects(id=self.agentId).first() # type: Optional[Agent]
  1230. if agent and agent.service_phone:
  1231. return agent.service_phone
  1232. elif self.servicePhone:
  1233. return self.servicePhone
  1234. else:
  1235. return self.username
  1236. @property
  1237. def can_withdraw_today(self):
  1238. if self.supports('in_withdraw_whitelist'):
  1239. return True
  1240. if self.devCount >= 5:
  1241. return True
  1242. return WithdrawRecord.count_today(ownerId = str(self.id), role = self.role) <= 2
  1243. @property
  1244. def my_agent(self):
  1245. # type:()->Agent
  1246. if not hasattr(self, '__my_agent__'):
  1247. my_agent = Agent.objects(id=self.agentId).first()
  1248. setattr(self, '__my_agent__', my_agent)
  1249. return getattr(self, '__my_agent__')
  1250. @property
  1251. def force_follow_gzh(self):
  1252. if self.forceFollowGzh == 'agent':
  1253. isNeedFollow = self.my_agent.forceFollowGzh
  1254. else:
  1255. isNeedFollow = True if self.forceFollowGzh == 'yes' else False
  1256. return isNeedFollow
  1257. @property
  1258. def show_auth_window(self):
  1259. showAuth = 0 if self.supports('not_show_auth_window') else 1
  1260. if showAuth == 0:
  1261. return False
  1262. else:
  1263. return True
  1264. @property
  1265. def productAgent(self):
  1266. if not hasattr(self, '__product_agent__'):
  1267. agent = get_user_manager_agent(self)
  1268. setattr(self, '__product_agent__', agent)
  1269. return getattr(self, '__product_agent__')
  1270. def freeze_ledger_balance(self, money, source_key, transaction_id):
  1271. """
  1272. 消费订单分账前的冻结
  1273. """
  1274. income_type = DEALER_INCOME_TYPE.DEVICE_INCOME
  1275. self._freeze_balance(income_type, money, source_key, transaction_id, "inhandLedger")
  1276. def clear_ledger_balance(self, transaction_id):
  1277. """
  1278. 分账记录收益完成 回滚
  1279. """
  1280. self._clear_frozen_balance(transaction_id, "inhandLedger")
  1281. class DealerDict(dict):
  1282. """
  1283. 经销商的缓存表示
  1284. """
  1285. def __repr__(self):
  1286. return '<DealerDict id=%s>' % (self.get('groupId', 'unknown'),)
  1287. @property
  1288. def v(self):
  1289. return dict(self)
  1290. @property
  1291. def id(self):
  1292. return self.get('id')
  1293. @property
  1294. def agentId(self):
  1295. return self.get('agentId')
  1296. @property
  1297. def entity(self):
  1298. if hasattr(self, '__entity__'):
  1299. return getattr(self, '__entity__')
  1300. dealer = Dealer.objects(id = self.id).first()
  1301. if not dealer:
  1302. raise Exception('no such dealer<id={}>'.format(self.id))
  1303. else:
  1304. setattr(self, '__entity__', dealer)
  1305. return dealer
  1306. @property
  1307. def currency_mode(self):
  1308. if 'currencyMode' not in self:
  1309. Dealer.invalid_cache(self.id)
  1310. dealer = Dealer.get_dealer(self.id) # type: DealerDict
  1311. self.update(dealer.v)
  1312. return self.get('currencyMode')
  1313. @property
  1314. def supportedConsumptionShow(self):
  1315. """
  1316. 客户端的 消费消息显示开关
  1317. """
  1318. if 'supportedConsumptionShow' not in self:
  1319. Dealer.invalid_cache(self.id)
  1320. dealer = Dealer.get_dealer(self.id)
  1321. self.update(dealer.v)
  1322. return self["supportedConsumptionShow"]
  1323. def is_currency(self, left_group, right_group):
  1324. # type: (GroupDict, GroupDict)->bool
  1325. if left_group.ownerId != right_group.ownerId:
  1326. return False
  1327. if self.currency_mode == 'allNo':
  1328. return False
  1329. if self.currency_mode == 'allYes':
  1330. return True
  1331. if left_group.currencyGroup and right_group.currencyGroup and (
  1332. left_group.currencyGroup == right_group.currencyGroup):
  1333. return True
  1334. else:
  1335. return False
  1336. def get_currency_group_ids(self, group, groups = None):
  1337. # type: (Optional[Group, GroupDict], List[Optional[Group, GroupDict]])->list
  1338. if self.currency_mode == 'allNo':
  1339. return []
  1340. else:
  1341. if not groups:
  1342. groups = Group.get_groups_of_dealer(str(self.id))
  1343. if self.currency_mode == 'allYes':
  1344. rv = []
  1345. for item in groups: # type: GroupDict
  1346. if item.groupId == group.groupId:
  1347. continue
  1348. rv.append(item.groupId)
  1349. return rv
  1350. else:
  1351. if not group.currencyGroup:
  1352. return []
  1353. else:
  1354. rv = []
  1355. for item in groups: # type: GroupDict
  1356. if item.groupId == group.groupId:
  1357. continue
  1358. if item.currencyGroup == group.currencyGroup:
  1359. rv.append(item.groupId)
  1360. return rv
  1361. @property
  1362. def servicePhone(self):
  1363. return self['servicePhone']
  1364. @property
  1365. def my_agent(self):
  1366. if '__my_agent__' not in self:
  1367. my_agent = Agent.objects(id=self.agentId).first()
  1368. self['__my_agent__'] = my_agent
  1369. return self.get('__my_agent__')
  1370. @property
  1371. def username(self):
  1372. return self['username']
  1373. @property
  1374. def nickname(self):
  1375. return self['nickname']
  1376. # 经销商商户相关信息
  1377. class Merchant(DynamicDocument):
  1378. ownerId = StringField(verbose_name = "经销商ID", default = "")
  1379. merchantType = StringField(verbose_name = "商户类型", default = "")
  1380. merchantName = StringField(verbose_name = "持卡人姓名", default = "")
  1381. provinceID = StringField(verbose_name = "省id", default = "")
  1382. provinceName = StringField(verbose_name = "省名称", default = "")
  1383. cityID = StringField(verbose_name = "市id", default = "")
  1384. cityName = StringField(verbose_name = "市名称", default = "")
  1385. address = StringField(verbose_name = "详细地址", default = "")
  1386. localLicensePhoto = StringField(verbose_name = "营业执照", default = "")
  1387. legalPerson = StringField(verbose_name = "负责人姓名", default = "")
  1388. idCode = StringField(verbose_name = "身份证", default = "")
  1389. email = StringField(verbose_name = "邮箱地址", default = "")
  1390. principalMobile = StringField(verbose_name = "商店名称", default = "")
  1391. validateCode = StringField(verbose_name = "商店名称", default = "")
  1392. localIndentityPhoto1 = StringField(verbose_name = "身份证第一面", default = "")
  1393. localIndentityPhoto2 = StringField(verbose_name = "身份证第二面", default = "")
  1394. accountCode = StringField(verbose_name = "公司账号银行账号", default = "")
  1395. parentBankName = StringField(verbose_name = "银行名称", default = "")
  1396. subBankName = StringField(verbose_name = "银行支行名称", default = "")
  1397. cardType = StringField(verbose_name = "卡类型", default = "")
  1398. bankCardType = StringField(verbose_name = "银行卡内部卡类型", default = "")
  1399. bankProvinceID = StringField(verbose_name = "开户地区省id", default = "")
  1400. bankProvinceName = StringField(verbose_name = "开户地区省name", default = "")
  1401. bankCityID = StringField(verbose_name = "开户地区市id", default = "")
  1402. bankCityName = StringField(verbose_name = "开户地区市name", default = "")
  1403. cnapsCode = StringField(verbose_name = "开户地区市name", default = "")
  1404. localAccountPhoto = StringField(verbose_name = "开户许可证", default = "")
  1405. getPicUrl = StringField(verbose_name = "URl", default = "")
  1406. dateTimeAdded = DateTimeField(default = datetime.datetime.now, verbose_name = '添加进来的时间')
  1407. manual = BooleanField(verbose_name = u"该卡是否仅手动提现", default = False)
  1408. accountType = StringField(verbose_name = u'卡类型(个人账号,对公账号)', default = BankCard.AccountType.PERSONAL)
  1409. # allowed = BooleanField(verbose_name=u"是否是提现的卡", default=False)
  1410. meta = {"collection": "Merchant", "db_alias": "default"}
  1411. @classmethod
  1412. def get_collection(cls):
  1413. return cls._get_collection()
  1414. # 超管页面获取对公账号信息
  1415. @property
  1416. def get_bankAccountInfo(self):
  1417. dic = dict()
  1418. # 卡号
  1419. dic["accountCode"] = self.accountCode
  1420. # 银行名称
  1421. dic["parentBankName"] = self.parentBankName
  1422. # 支行名称
  1423. dic["subBankName"] = self.subBankName
  1424. # 账户名称
  1425. dic["merchantName"] = self.merchantName
  1426. # 卡号类型
  1427. dic["cardType"] = self.cardType
  1428. # 是否对公
  1429. dic["isPublic"] = True if self.accountType == BankCard.AccountType.PUBLIC else False
  1430. dic['bankCityID'] = self.bankCityID
  1431. dic['bankProvinceID'] = self.bankProvinceID
  1432. return dic
  1433. # 插入银行信息 使用该函数前要做数据校验({字段:值})
  1434. def set_bankAccountInfo(self, kwargs):
  1435. for i in kwargs.keys():
  1436. if hasattr(self, i):
  1437. setattr(self, i, kwargs[i])
  1438. else:
  1439. continue
  1440. self.save()
  1441. # def setWithdrawCard(self):
  1442. # """
  1443. # 设置此卡为提现卡
  1444. # """
  1445. # self.__class__.objects.filter(ownerId=self.ownerId, id__ne=self.id).update(allowed=False)
  1446. # self.allowed = True
  1447. # self.save()
  1448. class UpscoreRecord(Document):
  1449. logicalCode = StringField(verbose_name = "设备逻辑编号", default = '')
  1450. devNo = StringField(verbose_name = "设备编号")
  1451. devType = StringField(verbose_name = "设备类型", default = '')
  1452. ownerId = StringField(verbose_name = "所有者", default = "")
  1453. time = DateTimeField(verbose_name = "时间", default = datetime.datetime.now)
  1454. score = IntField(verbose_name = "上分数量", default = 0)
  1455. groupName = StringField(verbose_name = "上分分组名称", default = "")
  1456. address = StringField(verbose_name = "上分设备地址", default = "")
  1457. remark = StringField(verbose_name = "备注", default = "")
  1458. package = DictField(verbose_name = "套餐", default = {})
  1459. type = StringField(verbose_name = '上分类型(套餐上分,远程充卡),默认为空,表示上分', default = '')
  1460. meta = {"collection": "UpscoreRecord", "db_alias": "logdata",
  1461. # "shard_key":("ownerId",)
  1462. }
  1463. @classmethod
  1464. def get_collection(cls):
  1465. return cls._get_collection()
  1466. class AdjustUserVirtualCardRecord(Document):
  1467. cardId = StringField(verbose_name = "虚拟卡ID")
  1468. cardNo = StringField(verbose_name = "虚拟卡卡号")
  1469. adjustDays = IntField(verbose_name = "调整天数", default = 0)
  1470. beforeAdjust = DateTimeField(verbose_name = "调整前的过期时间")
  1471. adjustQuota = ListField(verbose_name = "调整的额度", default = [])
  1472. oldQuota = ListField(verbose_name = "调整之前的额度", default = [])
  1473. operator = StringField(verbose_name = "操作人")
  1474. dealerId = StringField(verbose_name = "卡的管理者")
  1475. adjustType = StringField(verbose_name = "调整方式", choices = TYPE_ADJUST_USER_VIRTUAL_CARD.choices())
  1476. dateTimeAdded = DateTimeField(verbose_name = "添加时间", default = datetime.datetime.now)
  1477. meta = {'collection': 'adjust_user_virtual_card_record', 'db_alias': 'logdata'}
  1478. @classmethod
  1479. def get_collection(cls):
  1480. return cls._get_collection()
  1481. class DealerRechargeRecord(Searchable):
  1482. """
  1483. 经销商充值记录
  1484. """
  1485. class PayState(IterConstant):
  1486. UnPaid = 'UnPaid'
  1487. Paid = 'Paid'
  1488. Failure = 'Failure'
  1489. Fake = 'Fake'
  1490. Cancel = 'Cancel'
  1491. Quit = 'Quit'
  1492. Close = 'Close'
  1493. class ProductType(IterConstant):
  1494. SimCard = 'WF4801' # 流量卡充值
  1495. ApiCost = 'WF48A' # API接入
  1496. DisableAd = 'WF48D' # 禁用广告接入
  1497. AutoSimCard = 'WF4802' # 流量卡自动充值
  1498. ManualSimCard = 'WF4803' # 流量卡线下充值
  1499. SimOrderVerify = 'WF5801' # 流量卡充值对账后重新分账
  1500. ProductDesc = {
  1501. ProductType.SimCard: cn(u'流量卡充值'),
  1502. ProductType.AutoSimCard: cn(u'流量卡自动充值'),
  1503. ProductType.ManualSimCard: cn(u'流量卡线下充值'),
  1504. ProductType.ApiCost: cn(u'API配额充值'),
  1505. ProductType.DisableAd: cn(u'纯净计划'),
  1506. ProductType.SimOrderVerify: cn(u'流量卡充值对账'),
  1507. }
  1508. MapSubType = {
  1509. DealerPaySubType.AUTO_SIM_CARD: ProductType.AutoSimCard,
  1510. DealerPaySubType.MANUAL_SIM_CARD: ProductType.ManualSimCard,
  1511. DealerPaySubType.SIM_CARD: ProductType.SimCard,
  1512. DealerPaySubType.API_COST: ProductType.ApiCost,
  1513. DealerPaySubType.DISABLE_AD: ProductType.DisableAd,
  1514. DealerPaySubType.SIM_ORDER_VERIFY: ProductType.SimOrderVerify
  1515. }
  1516. MY_MAIN_TYPE = OrderMainType.PAY
  1517. orderNo = StringField(verbose_name = u"订单号", unique = True)
  1518. wxOrderNo = StringField(verbose_name = u"渠道订单号. 聚合商户,是聚合平台商户订单号;直连商户则是微信或支付宝订单号", default = "")
  1519. transactionId = StringField(verbose_name = u"微信或者支付宝订单号", default = "")
  1520. product = StringField(verbose_name = u'产品名称', default = ProductType.SimCard)
  1521. items = ListField(verbose_name = u"充值商品", null = False)
  1522. name = StringField(verbose_name = u'商品名称', default = '')
  1523. dealerId = StringField(verbose_name = u"经销商ID", null = False)
  1524. nickname = StringField(verbose_name = u"昵称", default = '')
  1525. totalFee = IntField(verbose_name = u"订单金额(以分为单位)", min_value = 0, null = False)
  1526. settleInfo = DictField(verbose_name = u'结算信息', default = {})
  1527. status = StringField(verbose_name = u"充值结果", default = PayState.UnPaid)
  1528. description = StringField(verbose_name = u"订单错误结果描述,一般为第三方错误码", default = "")
  1529. createdTime = DateTimeField(default = datetime.datetime.now, verbose_name = u"生成时间")
  1530. finishedTime = DateTimeField(default = None, verbose_name = u"支付完成时间")
  1531. attachParas = DictField(verbose_name = u"消费模型信息", default = {})
  1532. gateway = StringField(verbose_name = u'支付网关类型', default = '')
  1533. payAppType = StringField(verbose_name = u'支付应用类型', default = '')
  1534. payGatewayKey = StringField(verbose_name = u'支付网关key', default = '')
  1535. withdrawSourceKey = StringField(verbose_name = u'提现网关source key', default = '')
  1536. subject = StringField(verbose_name = u'商品标题', default = '')
  1537. extraInfo = DictField(verbose_name = u"订单本身模型信息", default = None)
  1538. meta = {'collection': 'dealer_recharge_record', 'db_alias': 'default',
  1539. 'indexes': [
  1540. {
  1541. 'fields': ['dealerId', 'product']
  1542. },
  1543. 'orderNo', 'wxOrderNo', 'transactionId',
  1544. # 'name'
  1545. ]}
  1546. search_fields = ('wxOrderNo', 'name')
  1547. def __repr__(self):
  1548. return '<DealerRechargeRecord id=%s>' % (str(self.id))
  1549. @classmethod
  1550. def get_product(cls, sub_type):
  1551. return {
  1552. 'type': cls.MapSubType[sub_type],
  1553. 'desc': cls.ProductDesc[cls.MapSubType[sub_type]]
  1554. }
  1555. @classmethod
  1556. def issue(cls, sub_type, payment_gateway, user, identifier=None, **payload):
  1557. # type: (str, PaymentGateway, CapitalUser, basestring, Dict)->DealerRechargeRecord
  1558. payload.update({
  1559. 'product': cls.get_product(sub_type)['type'],
  1560. 'gateway': payment_gateway.gateway_type,
  1561. 'payAppType': payment_gateway.pay_app_type,
  1562. 'payGatewayKey': payment_gateway.gateway_key,
  1563. 'subject': cls.get_product(sub_type)['desc'],
  1564. 'createdTime': datetime.datetime.now()
  1565. })
  1566. payload.update({
  1567. 'withdrawSourceKey': payment_gateway.withdraw_source_key()
  1568. })
  1569. if 'extraInfo' not in payload:
  1570. payload['extraInfo'] = {}
  1571. if payment_gateway.pay_app_type == PayAppType.SAOBEI:
  1572. payload['extraInfo'].update({
  1573. 'payTime': str(datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
  1574. })
  1575. payload['status'] = cls.PayState.UnPaid
  1576. identifier = identifier if identifier else str(user.id)
  1577. payload['orderNo'] = OrderNoMaker.make_order_no_32(
  1578. identifier = identifier, main_type = cls.MY_MAIN_TYPE, sub_type = sub_type)
  1579. record = cls(**payload)
  1580. record.save()
  1581. return record
  1582. @classmethod
  1583. def get_record(cls, order_no, dealer_id = None):
  1584. # type: (str, str)->Optional[DealerRechargeRecord]
  1585. # TODO: 分片需要带上片键
  1586. record = cls.objects(orderNo = order_no).first()
  1587. return record
  1588. def succeed(self, wxOrderNo, **kwargs):
  1589. # 这里和数据库强相关, 通过判断是否正确更新记录
  1590. payload = {
  1591. 'wxOrderNo': wxOrderNo
  1592. }
  1593. if kwargs:
  1594. payload.update(kwargs)
  1595. payload.update({'finishedTime': datetime.datetime.now()})
  1596. payload.update({'status': self.PayState.Paid})
  1597. result = DealerRechargeRecord.get_collection().update_one(
  1598. filter = {'_id': ObjectId(self.id), 'status': {'$ne': self.PayState.Paid}},
  1599. update = {'$set': payload},
  1600. upsert = False
  1601. )
  1602. return result.matched_count == 1
  1603. def fail(self, **kwargs):
  1604. self.status = self.PayState.Failure
  1605. self.finishedTime = datetime.datetime.now()
  1606. for key, value in kwargs.items():
  1607. setattr(self, key, value)
  1608. self.save()
  1609. return self
  1610. def cancel(self):
  1611. result = DealerRechargeRecord.get_collection().update_one(
  1612. filter = {'_id': ObjectId(self.id), 'status': self.PayState.UnPaid},
  1613. update = {'$set': {'status': self.PayState.Cancel,
  1614. 'finishedTime': datetime.datetime.now()}},
  1615. upsert = False
  1616. )
  1617. return result.matched_count == 1
  1618. def quit(self):
  1619. self.status = self.PayState.Quit
  1620. self.save()
  1621. return self
  1622. def close(self):
  1623. self.status = self.PayState.Close
  1624. self.finishedTime = datetime.datetime.now()
  1625. return self
  1626. def is_success(self):
  1627. return self.status == self.PayState.Paid
  1628. def is_fail(self):
  1629. return self.status == self.PayState.Failure
  1630. def is_cancel(self):
  1631. return self.status == self.PayState.Cancel
  1632. def is_unpay(self):
  1633. return self.status == self.PayState.UnPaid
  1634. def is_close(self):
  1635. return self.status == self.PayState.Close
  1636. @property
  1637. def sum_of_price(self):
  1638. # type: ()->RMB
  1639. return sum_rmb([RMB(_['price']) for _ in self.items])
  1640. def get_body(self):
  1641. return self.ProductDesc.get(self.product)
  1642. @property
  1643. def fen_total_fee(self):
  1644. return self.totalFee
  1645. @property
  1646. def my_gateway(self):
  1647. return self.gateway
  1648. @property
  1649. def pay_app_type(self):
  1650. return self.payAppType
  1651. @property
  1652. def pay_gateway_key(self):
  1653. return self.payGatewayKey
  1654. @property
  1655. def withdraw_source_key(self):
  1656. return self.withdrawSourceKey
  1657. class RefundDealerRechargeRecord(Searchable):
  1658. class Status(object):
  1659. """ 退款单的状态 正常流程顺序是从上至下 """
  1660. CREATED = 'created' # 创建
  1661. PROCESSING = 'processing' # 申请中
  1662. FAILURE = 'failure' # 申请失败 (彻底失败 需要重新发起)
  1663. SUCCESS = 'success' # 退款成功 (异步结果)
  1664. CLOSED = 'closed' # 退款失败 (异步结果 不需要重新发起)
  1665. # 订单常见的状态
  1666. # created ---> processing ---> success (申请 然后接受成功)
  1667. # created ---> failure (直接申请失败)
  1668. # created ---> processing ---> closed (申请成功 回调失败)
  1669. # created ---> processing ---> failure (申请成功 回调失败 不可以重试)
  1670. rechargeObjId = ObjectIdField(verbose_name = u'充值单')
  1671. orderNo = StringField(verbose_name = u'商户退款订单号', default = '')
  1672. # refundSeq = IntField(verbose_name=u'多次退款,计算退款序列号', default=1)
  1673. errorCode = StringField(verbose_name = u"错误代码", default = "")
  1674. errorDesc = StringField(verbose_name = u"错误描述", default = "")
  1675. money = MonetaryField(verbose_name = u"退款的金钱数额", default = RMB('0.00'))
  1676. status = StringField(verbose_name = u'订单状态', default = 'created')
  1677. datetimeAdded = DateTimeField(verbose_name = u"退款创建时间", default = datetime.datetime.now)
  1678. datetimeUpdated = DateTimeField(verbose_name = u"退款更新时间")
  1679. finishedTime = DateTimeField(verbose_name = u"退款到账时间")
  1680. tradeRefundNo = StringField(verbose_name = u"交易机构退款单号", default = "")
  1681. meta = {
  1682. 'collection': 'refund_dealer_recharge_record',
  1683. 'db_alias': 'default'
  1684. }
  1685. @classmethod
  1686. def issue(cls, order, refundCash):
  1687. # type:(DealerRechargeRecord, RMB)->RefundDealerRechargeRecord
  1688. refund_order_no = OrderNoMaker.make_order_no_32(
  1689. identifier = order.orderNo,
  1690. main_type = OrderMainType.REFUND,
  1691. sub_type = RefundSubType.REFUND)
  1692. return cls(
  1693. rechargeObjId = order.id,
  1694. # refundSeq=next_seq,
  1695. orderNo = refund_order_no,
  1696. money = refundCash,
  1697. status = cls.Status.CREATED).save()
  1698. def succeed(self, tradeRefundNo, finishedTime): # type:(Optional[str], datetime.datetime) -> bool
  1699. """ 更新为成功状态 已经成功退款 """
  1700. result = self.__class__.get_collection().update_one(
  1701. {
  1702. 'orderNo': self.orderNo,
  1703. 'status': self.Status.PROCESSING
  1704. },
  1705. {
  1706. '$set': {
  1707. 'status': self.Status.SUCCESS,
  1708. 'datetimeUpdated': datetime.datetime.now(),
  1709. 'tradeRefundNo': tradeRefundNo,
  1710. 'finishedTime': finishedTime
  1711. }
  1712. }
  1713. )
  1714. return result.matched_count == 1
  1715. def fail(self, errorCode = "", errorDesc = ""): # type:(str, unicode) -> bool
  1716. """
  1717. 更新为失败状态 表示退款申请失败 这种情况下 可能就需要售后进行介入
  1718. 1. 订单申请发起时候即失败
  1719. 2. 订单申请通过 但是回调的时候明确失败 并且是不可重试的失败
  1720. 3. 此状态即表示订单没退款 理论上可以重新发起退款
  1721. """
  1722. return self.__class__.objects.filter(
  1723. orderNo = self.orderNo,
  1724. status__in = [self.Status.PROCESSING, self.Status.CREATED]
  1725. ).update(
  1726. status = self.Status.FAILURE,
  1727. datetimeUpdated = datetime.datetime.now(),
  1728. errorCode = errorCode,
  1729. errorDesc = errorDesc
  1730. )
  1731. def closed(self, tradeRefundNo, errorCode = "", errorDesc = ""): # type:(str, str, str) -> bool
  1732. """
  1733. 退款申请成功了 表示合法的退款申请 但是由于某些原因业务进行不下去
  1734. """
  1735. result = self.__class__.objects.filter(
  1736. orderNo = self.orderNo,
  1737. status = self.Status.PROCESSING
  1738. ).update(
  1739. status = self.Status.CLOSED,
  1740. datetimeUpdated = datetime.datetime.now(),
  1741. tradeRefundNo = tradeRefundNo,
  1742. errorCode = errorCode,
  1743. errorDesc = errorDesc
  1744. )
  1745. return result
  1746. def processing(self): # type:() -> bool
  1747. result = self.__class__.objects.filter(
  1748. orderNo = self.orderNo,
  1749. status__in = [self.Status.CLOSED, self.Status.CREATED]
  1750. ).update(
  1751. status = self.Status.PROCESSING,
  1752. datetimeUpdated = datetime.datetime.now()
  1753. )
  1754. return result
  1755. @property
  1756. def is_fail(self):
  1757. """ 是否订单失败 """
  1758. return self.status == self.Status.FAILURE
  1759. @property
  1760. def is_closed(self):
  1761. """ 退款单是否已经关闭 目前这个状态没有用 适用于用户手动取消退款申请 """
  1762. return self.status == self.Status.CLOSED
  1763. @property
  1764. def is_success(self):
  1765. """ 退款已经成功 """
  1766. return self.status == self.Status.SUCCESS
  1767. @property
  1768. def is_apply(self):
  1769. """ 是否已经发出退款申请 """
  1770. return self.status in [self.Status.CREATED, self.Status.PROCESSING]
  1771. @property
  1772. def is_processing(self):
  1773. return self.status == self.Status.PROCESSING
  1774. @property
  1775. def is_successful(self):
  1776. """ 保留旧的方法 """
  1777. return self.status in [self.Status.SUCCESS, self.Status.PROCESSING]
  1778. @property
  1779. def pay_order(self):
  1780. # type: ()->DealerRechargeRecord
  1781. if not hasattr(self, '__pay_order__'):
  1782. pay_order = DealerRechargeRecord.objects(id = str(self.rechargeObjId)).first()
  1783. setattr(self, '__pay_order__', pay_order)
  1784. return getattr(self, '__pay_order__')
  1785. @pay_order.setter
  1786. def pay_order(self, order):
  1787. setattr(self, '__pay_order__', order)
  1788. @classmethod
  1789. def get_record(cls, order_no):
  1790. return cls.objects(orderNo = order_no).first()
  1791. class OnSale(DynamicDocument):
  1792. """
  1793. 优惠活动管理
  1794. """
  1795. dealerId = StringField(verbose_name = "经销商ID", null = False)
  1796. name = StringField(verbose_name = '活动名称', default = '')
  1797. showSite = IntField(verbose_name = "展现的位置", default = 0) # 展现的位置0,表示扫码后的套餐页面。
  1798. logicalCodeList = ListField(verbose_name = "需要推送的设备", null = False)
  1799. onsaleType = StringField(verbose_name = '活动类型', default = '')
  1800. detailDict = DictField(verbose_name = '活动详细数据', default = {})
  1801. desc = StringField(verbose_name = '描述', default = '')
  1802. img = StringField(verbose_name = '创意图片', default = '')
  1803. onClickUrl = StringField(verbose_name = '回调地址', default = '')
  1804. startTime = DateTimeField(verbose_name = '起始时间', default = datetime.datetime.now)
  1805. endTime = DateTimeField(verbose_name = '结束时间', default = datetime.datetime.now)
  1806. status = StringField(verbose_name = '状态', default = 'start')
  1807. #: 展现类型,onlyOne,点击后就不弹出。forever,表示即使点击过,还是一直弹出,web:表示是页面,不是图片
  1808. showType = StringField(verbose_name = '状态', default = 'onlyOne')
  1809. onsaleTypeDict = {
  1810. u'首次使用送金币': {
  1811. 'onClickUrl': '/user/promotion/getCoins',
  1812. 'desc': u'适用所有设备类型,用户首次扫码的时候,允许客户领金币进行体验,本次活动领过金币的用户,下次无法领取金币。',
  1813. 'expression': "",
  1814. 'img': '/app/img/marketing/first_give_coins.jpg',
  1815. 'showType': 'onlyOne'
  1816. },
  1817. u'首次免费按摩': {
  1818. 'onClickUrl': '/user/promotion/getDuration',
  1819. 'desc': u'适用按摩坐垫以及足疗机,用户首次扫码的时候,允许客户直接启动设备体验,本次活动体验过的用户,下次无法再体验。',
  1820. 'expression': "dev['devType']['code'] in ['100110','100111','100112','100113','100119','100900']",
  1821. 'img': '/app/img/marketing/first_free.jpg',
  1822. 'showType': 'onlyOne'
  1823. },
  1824. u'优惠充值大放送': {
  1825. 'onClickUrl': '/user/onsaleRecharge',
  1826. 'desc': u'适用所有设备类型,用户优惠充值,活动期间始终弹出此推广创意,即使用户已经充值优惠过了。(用户点击推广活动后,会跳转到对应设备的充值页面。需要提前在优惠设置中,把对应的地址下的充值套餐配置好)',
  1827. 'expression': "",
  1828. 'img': '/app/img/marketing/recharegeOnsale.jpg',
  1829. 'showType': 'forever'
  1830. },
  1831. u'手机实名免费用': {
  1832. 'onClickUrl': '/user/inputMobile',
  1833. 'desc': u'首次验证手机号后,可以免费使用设备',
  1834. 'expression': "",
  1835. 'img': '/app/img/marketing/inputMobile.jpg',
  1836. 'showType': 'onlyOne-web'
  1837. },
  1838. #u"京东新人1分购": {
  1839. # "onClickUrl": "/user/onsaleJdNewUser",
  1840. # "desc": u"",
  1841. # "expression": "",
  1842. # "img": "https://resource.washpayer.com/uploaded/marketing/jd.jpg",
  1843. # "showType": "onlyOne"
  1844. #},
  1845. u'包年包月优惠多多': {
  1846. 'onClickUrl': '/user/onsaleTicketList',
  1847. 'desc': u'适用所有设备类型,用户优惠充值,活动期间始终弹出此推广创意,即使用户已经充值优惠过了。(用户点击推广活动后,会跳转到对应设备的充值页面。需要提前在优惠设置中,把对应的地址下的充值套餐配置好)',
  1848. 'expression': "",
  1849. 'img': '/app/img/marketing/recharegeOnsale.jpg',
  1850. 'showType': 'forever'
  1851. },
  1852. }
  1853. def to_dict(self):
  1854. return {
  1855. 'id': str(self.id),
  1856. 'name': self.name,
  1857. 'showSite': self.showSite,
  1858. 'logicalCodeList': self.logicalCodeList,
  1859. 'onsaleType': self.onsaleType,
  1860. 'onClickUrl': self.onClickUrl,
  1861. 'desc': self.desc,
  1862. 'detailDict': self.detailDict,
  1863. 'img': self.img,
  1864. 'startTime': self.startTime.strftime("%Y-%m-%d"),
  1865. 'endTime': self.endTime.strftime("%Y-%m-%d"),
  1866. 'status': self.status
  1867. }
  1868. # 优惠活动访问记录
  1869. class OnSaleRecord(DynamicDocument):
  1870. onsaleId = StringField(verbose_name = "经销商ID", null = False)
  1871. userId = StringField(verbose_name = "用户ID", null = False)
  1872. nickName = StringField(verbose_name = "用户昵称", null = False)
  1873. addedTime = DateTimeField(verbose_name = "访问时间", default = datetime.datetime.now)
  1874. onsaleDetail = DictField(verbose_name = "活动详情数据", default = {})
  1875. meta = {
  1876. "collection": "on_sale_record",
  1877. "db_alias": "logdata",
  1878. }
  1879. #: 经销商创建卡卷
  1880. class VirtualCard(Searchable):
  1881. cardName = StringField(verbose_name = "卡名称", default = "")
  1882. ownerId = StringField(verbose_name = "卡的发布老板", default = "")
  1883. groupIds = ListField(verbose_name = "可用使用卡的地址", default = []) # 空表示没有地址,*表示所有地址下可用
  1884. devTypeList = ListField(verbose_name = "支持的设备类型清单", default = []) # 存放设备类型的ID
  1885. price = MonetaryField(verbose_name = "卡的售价", default = RMB('0.00'))
  1886. createTime = DateTimeField(verbose_name = "创建时间", default = datetime.datetime.now)
  1887. periodDays = FloatField(verbose_name = "卡的可用天数", default = 30.0)
  1888. expiredTime = DateTimeField(verbose_name="卡卷售卖下架时间", default=lambda : datetime.datetime.now() + datetime.timedelta(days=365))
  1889. dayQuota = ListField(verbose_name = "日限额度", default = []) # {'unit':u'次','count':2}
  1890. quota = ListField(verbose_name = "总的额度", default = [])
  1891. userLimit = IntField(verbose_name = "用户数量限制", default = 10)
  1892. userDesc = StringField(verbose_name = "用户侧的描述", default = "")
  1893. dealerDesc = StringField(verbose_name = "经销商侧描述", default = "")
  1894. status = IntField(verbose_name = "卡状态", default = 1) # -1:删除;0:停止发售;1:开始发售
  1895. # 作为内置参数,接口不暴露出来 使用的时候后台脚本修改, 修改脚本名称为 adjust_virtual_card_need_renew
  1896. needRenewMax = IntField(verbose_name = "过期后可续卡天数", default=10)
  1897. needRenewMin = IntField(verbose_name = "过期前可续卡天数", default=10)
  1898. # 暂停发售之后是否可以继续续卡
  1899. renewIgnoreStatus = BooleanField(verbose_name = "暂停发售之后是否可以继续续卡", default = False)
  1900. # 坤元虚拟卡特有:功率
  1901. power = IntField(verbose_name = "包月卡功率限制", default = 0)
  1902. meta = {
  1903. "collection": "VirtualCard",
  1904. "db_alias": "default",
  1905. }
  1906. @property
  1907. def is_expired(self):
  1908. return self.expiredTime <= datetime.datetime.now()
  1909. @property
  1910. def description(self):
  1911. """
  1912. 用于描述虚拟卡的信息
  1913. :return:
  1914. """
  1915. try:
  1916. qStr = u"额度:"
  1917. for item in self.quota:
  1918. qStr += "{} {}".format(item.get("count"), item.get("unit"))
  1919. tStr = u"时间:{}天".format(self.periodDays)
  1920. return "{}\t{}".format(qStr, tStr)
  1921. except Exception:
  1922. return None
  1923. @classmethod
  1924. def get_last(cls, ownerId):
  1925. return cls.objects.filter(ownerId=ownerId).order_by("-id").first()
  1926. @property
  1927. def groups(self):
  1928. if '*' in self.groupIds:
  1929. groups = '*'
  1930. else:
  1931. groups = [{'groupName': grp['groupName'], 'address': grp['address'], 'groupId': grp['groupId']} for grp in
  1932. Group.get_groups_by_group_ids(self.groupIds).values()]
  1933. return groups
  1934. @property
  1935. def devTypes(self):
  1936. devs = DeviceType.objects.filter(id__in=self.devTypeList).only("majorDeviceType", "name")
  1937. devTypes = list()
  1938. for _dev in devs: # type: DeviceType
  1939. devTypes.append({"devTypeName": _dev.name, "majorDeviceType": _dev.majorDeviceType})
  1940. return devTypes
  1941. def to_dict(self):
  1942. return {
  1943. "id": str(self.id),
  1944. "cardId": str(self.id),
  1945. "cardName": self.cardName,
  1946. "price": self.price,
  1947. "periodDays": self.periodDays,
  1948. "createTime": self.createTime,
  1949. "expiredTime": self.expiredTime,
  1950. "userDesc": self.userDesc,
  1951. "dealerDesc": self.dealerDesc,
  1952. "status": self.status,
  1953. 'isExpired': self.is_expired,
  1954. 'power': self.power,
  1955. "quota": self.quota,
  1956. "dayQuota": self.dayQuota,
  1957. "groups": self.groups,
  1958. "devTypes": self.devTypes,
  1959. 'userLimit': self.userLimit
  1960. }
  1961. class ElecPriceTemplate(Searchable):
  1962. ownerId = StringField(verbose_name = "卡的发布老板", default = "")
  1963. name = StringField(verbose_name = "卡的发布老板", default = "")
  1964. priceList = ListField(verbose_name = "48分时价格", default = [])
  1965. class ItemType(Searchable):
  1966. ownerId = StringField(verbose_name = "ownerId", default = "")
  1967. title = StringField(verbose_name = "标题", default = "")
  1968. desc = StringField(verbose_name = "描述", default = "")
  1969. picUrl = StringField(verbose_name = "图片", default = "")
  1970. price = IntField(verbose_name = "价格,分", default = 1)
  1971. class SubAccount(UserSearchable):
  1972. """
  1973. 子账号表
  1974. """
  1975. agentId = StringField(verbose_name = "agentId", min_length = 1)
  1976. phone = StringField(verbose_name = "电话", default = "")
  1977. bossId = ObjectIdField(verbose_name = "主账号ID")
  1978. permissionList = ListField(verbose_name = "菜单清单", default = [])
  1979. meta = {
  1980. 'collection': 'sub_account',
  1981. 'db_alias': 'default',
  1982. 'indexes': [
  1983. {
  1984. 'fields': ['username', 'bossId'], 'unique': True
  1985. },
  1986. {
  1987. 'fields': ['username', 'agentId'], 'unique': True
  1988. }
  1989. ],
  1990. }
  1991. def __str__(self):
  1992. return '{}<id={} username={} nickname={} agentId={}>'.format(
  1993. self.__class__.__name__, str(self.id), self.username, self.nickname, self.agentId)
  1994. @property
  1995. def myBoss(self):
  1996. # type:()->Dealer
  1997. if hasattr(self, '_myBoss'):
  1998. return getattr(self, '_myBoss')
  1999. else:
  2000. myBoss = Dealer.objects.get(id = self.bossId)
  2001. setattr(self, '_myBoss', myBoss)
  2002. return myBoss
  2003. def query_feature_by_list(self, queryList):
  2004. return self.myBoss.query_feature_by_list(queryList)
  2005. def query_home_page_layout(self):
  2006. return self.myBoss.query_home_page_layout()
  2007. def to_dict(self):
  2008. value = self.myBoss.to_dict()
  2009. value.update(
  2010. {
  2011. 'username': self.username,
  2012. 'nickname': self.nickname,
  2013. 'role': ROLE.subaccount
  2014. }
  2015. )
  2016. return value
  2017. @property
  2018. def switches(self):
  2019. return self.myBoss.switches
  2020. @property
  2021. def isManagerialOpenIdBound(self):
  2022. return self.myBoss.isManagerialOpenIdBound
  2023. def get_bound_pay_openid(self, key):
  2024. return self.myBoss.get_bound_pay_openid(key)
  2025. @property
  2026. def format_default_discount(self):
  2027. return self.myBoss.format_default_discount
  2028. def get_own_devices(self):
  2029. groupIds = Group.get_group_ids_of_dealer(str(self.bossId))
  2030. devNoList = Device.get_devNos_by_group(groupIds)
  2031. return Device.get_dev_by_nos(devNoList).values()
  2032. @property
  2033. def income_aggregate_source(self):
  2034. # type: ()->Dict[str, AnyStr]
  2035. """
  2036. e.g. {'ad': u'广告'}
  2037. :return:
  2038. """
  2039. g = itemgetter(*self.myBoss.supportedIncomeAggregate)
  2040. return dict(zip(self.myBoss.supportedIncomeAggregate, g(DEALER_INCOME_SOURCE_TRANSLATION)))
  2041. @property
  2042. def consumption_aggregate_source(self):
  2043. # type: ()->Dict[str, AnyStr]
  2044. """
  2045. e.g. {'elect': u'电量'}
  2046. :return:
  2047. """
  2048. g = itemgetter(*self.myBoss.supportedConsumptionAggregate)
  2049. return dict(zip(self.myBoss.supportedConsumptionAggregate, g(DEALER_CONSUMPTION_AGG_KIND_TRANSLATION)))
  2050. def set_agent_profit_share(self, share):
  2051. # type: (Percent)->int
  2052. """
  2053. :param share:
  2054. :return:
  2055. """
  2056. updated = self.myBoss.update(agentProfitShare = share.mongo_amount)
  2057. return updated
  2058. @property
  2059. def adShow(self):
  2060. return self.myBoss.adShow
  2061. @property
  2062. def ad_show(self):
  2063. return self.myBoss.ad_show
  2064. @property
  2065. def pushBrokerUrl(self):
  2066. return self.myBoss.pushBrokerUrl
  2067. @property
  2068. def isPurePartner(self):
  2069. return self.myBoss.isPurePartner
  2070. @property
  2071. def wechat(self):
  2072. return self.myBoss.wechat
  2073. @property
  2074. def description(self):
  2075. return self.myBoss.description
  2076. @property
  2077. def cardNo(self):
  2078. return self.myBoss.cardNo
  2079. @property
  2080. def bankName(self):
  2081. return self.myBoss.bankName
  2082. @property
  2083. def cardHolder(self):
  2084. return self.myBoss.cardHolder
  2085. @property
  2086. def isRead(self):
  2087. return self.myBoss.isRead
  2088. @property
  2089. def noPassReason(self):
  2090. return self.myBoss.noPassReason
  2091. @property
  2092. def cibMerchant(self):
  2093. return self.myBoss.cibMerchant
  2094. @property
  2095. def managerialAppId(self):
  2096. return self.myBoss.managerialAppId
  2097. @property
  2098. def managerialOpenId(self):
  2099. return self.myBoss.managerialOpenId
  2100. @property
  2101. def wechatAuthUserInfo(self):
  2102. return self.myBoss.wechatAuthUserInfo
  2103. @property
  2104. def managerialWechatBoundTime(self):
  2105. return self.myBoss.managerialWechatBoundTime
  2106. @property
  2107. def agentProfitShare(self):
  2108. return self.myBoss.agentProfitShare
  2109. @property
  2110. def beforeCharge(self):
  2111. return self.myBoss.beforeCharge
  2112. @property
  2113. def noRecharge(self):
  2114. return self.myBoss.noRecharge
  2115. @property
  2116. def defaultWashConfig(self):
  2117. return self.myBoss.defaultWashConfig
  2118. @property
  2119. def withdrawlNotify(self):
  2120. return self.myBoss.withdrawlNotify
  2121. @property
  2122. def offlineNotify(self):
  2123. return self.myBoss.offlineNotifySwitch
  2124. @property
  2125. def dailyIncomeReportPushSwitch(self):
  2126. return self.myBoss.dailyIncomeReportPushSwitch
  2127. @property
  2128. def newUserPaymentOrderPushSwitch(self):
  2129. return self.myBoss.newUserPaymentOrderPushSwitch
  2130. @property
  2131. def devFaultPushDealerSwitch(self):
  2132. return self.myBoss.devFaultPushDealerSwitch
  2133. @property
  2134. def devFaultPushUserSwitch(self):
  2135. return self.myBoss.devFaultPushUserSwitch
  2136. @property
  2137. def currency_mode(self):
  2138. return self.myBoss.currency_mode
  2139. @property
  2140. def serviceName(self):
  2141. return self.myBoss.serviceName
  2142. @property
  2143. def servicePhone(self):
  2144. return self.myBoss.servicePhone
  2145. @property
  2146. def qrcodeUrl(self):
  2147. return self.myBoss.qrcodeUrl
  2148. @property
  2149. def withdrawFeeRatio(self):
  2150. return self.myBoss.withdrawFeeRatio
  2151. @property
  2152. def annualTrafficCost(self):
  2153. return self.myBoss.annualTrafficCost
  2154. @property
  2155. def limitAdLocation(self):
  2156. return self.myBoss.limitAdLocation
  2157. @property
  2158. def defaultDiscountConfig(self):
  2159. return self.myBoss.defaultDiscountConfig
  2160. @property
  2161. def supportedIncomeAggregate(self):
  2162. return self.myBoss.supportedIncomeAggregate
  2163. @property
  2164. def supportedConsumptionAggregate(self):
  2165. return self.myBoss.supportedConsumptionAggregate
  2166. @property
  2167. def bottomAd(self):
  2168. return self.myBoss.bottomAd
  2169. @property
  2170. def limitDevNum(self):
  2171. return self.myBoss.limitDevNum
  2172. @property
  2173. def feature_boolean_map(self):
  2174. return self.myBoss.feature_boolean_map
  2175. @property
  2176. def features(self):
  2177. return self.myBoss.features
  2178. @property
  2179. def normal(self):
  2180. my_boss = self.myBoss # type: Dealer
  2181. if not my_boss.normal:
  2182. return my_boss.normal
  2183. else:
  2184. return self.status == self.Status.NORMAL
  2185. @property
  2186. def forbidden(self):
  2187. my_boss = self.myBoss # type: Dealer
  2188. if my_boss.forbidden:
  2189. return my_boss.forbidden
  2190. else:
  2191. return self.status == self.Status.FORBIDDEN
  2192. @property
  2193. def abnormal(self):
  2194. my_boss = self.myBoss # type: Dealer
  2195. if my_boss.abnormal:
  2196. return my_boss.abnormal
  2197. else:
  2198. return self.status == self.Status.ABNORMAL
  2199. @property
  2200. def no_withdraw(self):
  2201. return False
  2202. @property
  2203. def is_inhouse_wallet(self):
  2204. return False
  2205. def limit_filter_date(self, startTime, endTime):
  2206. my_boss = self.myBoss # type: Dealer
  2207. return my_boss.limit_filter_date(startTime, endTime)
  2208. @property
  2209. def service_phone(self):
  2210. my_boss = self.myBoss # type: Dealer
  2211. return my_boss.service_phone
  2212. class Complaint(Searchable):
  2213. openId = StringField(verbose_name = 'openId', default = '')
  2214. logicalCode = StringField(verbose_name = 'logicalCode', default = '')
  2215. groupName = StringField(verbose_name = 'groupName', default = '')
  2216. orderNo = StringField(verbose_name = 'orderNo', default = '')
  2217. handledStatus = StringField(verbose_name = 'status', default = 'init') # init/notice/warning/forbidden
  2218. reason = StringField(verbose_name = 'reason', default = '')
  2219. dateTimeAdded = DateTimeField(verbose_name = u'添加进来的时间', default = datetime.datetime.now)
  2220. handledTime = DateTimeField(verbose_name = u'添加进来的时间', default = datetime.datetime.now)
  2221. meta = {'collection': 'complaint', 'db_alias': 'logdata'}
  2222. # 发送给经销商的消息
  2223. class DealerMessage(Searchable):
  2224. ownerId = StringField(verbose_name = 'ownerId', default = '')
  2225. fromUser = StringField(verbose_name = 'fromUser', default = '')
  2226. messageType = StringField(verbose_name = 'messageType', default = '')
  2227. title = StringField(verbose_name = 'title', default = '')
  2228. desc = StringField(verbose_name = 'desc', default = '')
  2229. read = BooleanField(verbose_name = 'status', default = False)
  2230. dateTimeAdded = DateTimeField(verbose_name = u'添加时间', default = datetime.datetime.now)
  2231. readTime = DateTimeField(verbose_name = u'添加时间', default = datetime.datetime.now)
  2232. relatedInfo = DictField(verbose_name = u'关联信息,便于查找,比如投诉信息', default = {})
  2233. meta = {'collection': 'dealer_message', 'db_alias': 'logdata'}
  2234. # 经销商的换货单
  2235. class ExchangeOrder(Searchable):
  2236. factoryId = ObjectIdField(verbose_name = 'factoryId', default = '')
  2237. agentId = ObjectIdField(verbose_name = 'agentId', default = '')
  2238. ownerId = ObjectIdField(verbose_name = 'ownerId', default = '')
  2239. dateTimeAdded = DateTimeField(verbose_name = 'dateTimeAdded', default = datetime.datetime.now())
  2240. dealerStatus = StringField(verbose_name = u'经销商侧状态', default = '') # 比如已提单、已发货等
  2241. partIds = ListField(verbose_name = u'换货单', default = [])
  2242. pics = ListField(verbose_name = u'用户上传的图片', default = [])
  2243. dealerWords = StringField(verbose_name = u'经销商留言', default = '')
  2244. factoryAddr = DictField(verbose_name = u'厂家维修地址', default = {})
  2245. dealerOrderNo = StringField(verbose_name = u'经销商的快递单号', default = '')
  2246. factoryStatus = StringField(verbose_name = u'厂家侧的状态', default = '') # 比如已发货、已同意换货等
  2247. factoryOrderNo = StringField(verbose_name = u'厂家的快递单号', default = '')
  2248. dealerAddr = DictField(verbose_name = u'经销商地址', default = {})
  2249. factoryWords = StringField(verbose_name = u'厂家留言', default = '')
  2250. more = StringField(verbose_name = u'备注', default = '')
  2251. meta = {'collection': 'exchange_order', 'db_alias': 'default'}
  2252. search_fields = ('dealerWords', 'factoryWords', 'factoryOrderNo', 'dealerOrderNo')
  2253. def make_status_info(self):
  2254. dealerInfo = ''
  2255. factoryInfo = ''
  2256. if self.dealerStatus == 'created':
  2257. dealerInfo = u'经销商申请换板'
  2258. elif self.dealerStatus == 'sended':
  2259. dealerInfo = u'经销商已发货'
  2260. if self.factoryStatus == 'agreed':
  2261. factoryInfo = u'厂家同意换货'
  2262. elif self.factoryStatus == 'disagreed':
  2263. factoryInfo = u'厂家不同意'
  2264. elif self.factoryStatus == 'sended':
  2265. factoryInfo = u'售后中心已发货'
  2266. elif self.factoryStatus == 'closed':
  2267. factoryInfo = u'成功关闭'
  2268. elif self.factoryStatus == '':
  2269. factoryInfo = u'待确认'
  2270. if dealerInfo and factoryInfo:
  2271. return '%s,%s' % (dealerInfo, factoryInfo)
  2272. elif dealerInfo:
  2273. return dealerInfo
  2274. else:
  2275. return factoryInfo
  2276. # 卡的上分记录表
  2277. class UpCardScoreRecord(Searchable):
  2278. cardId = StringField(verbose_name = "cardId", default = '')
  2279. cardNo = StringField(verbose_name = "cardNo", default = '')
  2280. ownerId = StringField(verbose_name = "所有者", default = "")
  2281. dateTimeAdded = DateTimeField(verbose_name = "时间", default = datetime.datetime.now)
  2282. score = FloatField(verbose_name = "上分数量", default = 0.0)
  2283. address = StringField(verbose_name = "绑定地址", default = "")
  2284. remark = StringField(verbose_name = "备注", default = "")
  2285. meta = {"collection": "UpCardScoreRecord", "db_alias": "logdata"}
  2286. @classmethod
  2287. def get_collection(cls):
  2288. return cls._get_collection()
  2289. class PermissionInfo(Searchable): # 权限模版
  2290. pid = StringField(verbose_name = 'pid', null = True, auto_incre = True)
  2291. key = StringField(verbose_name = 'key', unique = True, null = False)
  2292. value = BooleanField(verbose_name = 'value', default = False)
  2293. name = StringField(verbose_name = '权限名称', default = '')
  2294. desc = StringField(verbose_name = '权限详情', default = '')
  2295. dateTimeAdded = DateTimeField(verbose_name = '添加时间', default = datetime.datetime.now)
  2296. meta = {
  2297. 'collection': 'permission_info',
  2298. 'db_alias': 'default',
  2299. }
  2300. @classmethod
  2301. def get_permissions(cls):
  2302. def run(head):
  2303. if head.count() == 0:
  2304. return
  2305. res = {}
  2306. for item in head:
  2307. value = run(all_Permission.filter(pid = str(item.id))) # 结果为None or dict
  2308. res[item.key] = value or item.value
  2309. return res
  2310. all_Permission = cls.objects.all()
  2311. head = all_Permission.filter(pid = None)
  2312. return run(head)
  2313. class PermissionRole(Searchable): # 成员表
  2314. AUTHORIZE_TYPE = ['dealer_to_dealer', 'dealer_to_subAccount']
  2315. roleName = StringField(verbose_name='角色名称', null=True, default='')
  2316. operId = StringField(verbose_name='操作人ID')
  2317. dealerId = StringField(verbose_name='经销商ID')
  2318. subAccountId = StringField(verbose_name='子账号ID')
  2319. permissionRuleId = StringField(verbose_name='权限配置')
  2320. dateTimeAdded = DateTimeField(verbose_name='添加时间', default=datetime.datetime.now)
  2321. lastModifiedTime = DateTimeField(verbose_name='最后一次操作时间', default=datetime.datetime.now)
  2322. authorizeType = StringField(verbose_name='授权关系',choices = AUTHORIZE_TYPE)
  2323. isActive = BooleanField(verbose_name='激活', default=False)
  2324. meta = {
  2325. 'collection': 'permission_role',
  2326. 'db_alias': 'default',
  2327. }
  2328. @staticmethod
  2329. def add_dealer_role(oper_id, dealerId):
  2330. rule = PermissionRule.objects.create(ruleName=str(uuid.uuid4()), dealerId=dealerId,
  2331. permissionDict=PermissionInfo.get_permissions())
  2332. models = {
  2333. 'roleName': None,
  2334. 'operId': oper_id,
  2335. 'dealerId': dealerId,
  2336. 'permissionRuleId': str(rule.id),
  2337. 'authorizeType': 'dealer_to_dealer',
  2338. }
  2339. return PermissionRole.objects.create(**models)
  2340. @staticmethod
  2341. def add_subAccount_role(oper_id, dealerId):
  2342. rule = PermissionRule.objects.create(ruleName=str(uuid.uuid4()), dealerId=dealerId,
  2343. permissionDict=PermissionInfo.get_permissions())
  2344. models = {
  2345. 'roleName': None,
  2346. 'operId': oper_id,
  2347. 'dealerId': dealerId,
  2348. 'permissionRuleId': str(rule.id),
  2349. 'authorizeType': 'dealer_to_subAccount',
  2350. }
  2351. return PermissionRole.objects.create(**models)
  2352. def save(self, **kw):
  2353. self.lastModifiedTime = datetime.datetime.now()
  2354. return super(PermissionRole, self).save(**kw)
  2355. @classmethod
  2356. def get_role_permission(cls, dealerId, operId):
  2357. cacheKey = '{}-{}'.format(dealerId, operId)
  2358. permissionRule = cache.get(cacheKey)
  2359. if not permissionRule:
  2360. role = cls.objects.filter(dealerId=dealerId, operId=operId, isActive=True).first()
  2361. permissionRule = {}
  2362. if role:
  2363. permissionRule = PermissionRule.objects.get(id=role.permissionRuleId).permissionDict
  2364. cache.set(cacheKey, permissionRule)
  2365. return permissionRule
  2366. @classmethod
  2367. def delete_role_permission(cls, operIds, dealerId):
  2368. cls.objects.filter(operId__in=operIds, dealerId=dealerId).update(isActive=False)
  2369. for operId in operIds:
  2370. cacheKey = '{}-{}'.format(dealerId, operId)
  2371. cache.delete(cacheKey)
  2372. @classmethod
  2373. def get_auth_to_dealer(cls, dealerId):
  2374. """
  2375. 获取 本账号授权的经销商
  2376. :param dealerId:
  2377. :return:
  2378. """
  2379. return cls.objects.filter(dealerId=dealerId, authorizeType='dealer_to_dealer', isActive=True).values_list(
  2380. 'operId')
  2381. @classmethod
  2382. def get_auth_to_sub(cls, dealerId):
  2383. """
  2384. 获取 本账号授权的子账号
  2385. :param dealerId:
  2386. :return:
  2387. """
  2388. return cls.objects.filter(dealerId=dealerId, authorizeType='dealer_to_subAccount', isActive=True).values_list(
  2389. 'operId')
  2390. @classmethod
  2391. def get_is_auth_list(cls, operId, authorizeType):
  2392. """
  2393. :param operId: 操作人ID
  2394. :param authorizeType: 授权类型
  2395. :return:
  2396. """
  2397. return cls.objects.filter(operId=str(operId), authorizeType=authorizeType, isActive=True).values_list('dealerId')
  2398. class PermissionRule(Searchable): # 权限配置表
  2399. ruleName = StringField(verbose_name = '配置名称')
  2400. dealerId = StringField(verbose_name = '经销商')
  2401. permissionDict = DictField(verbose_name = '权限内容')
  2402. dateTimeAdded = DateTimeField(verbose_name = '添加时间', default = datetime.datetime.now)
  2403. lastModifiedTime = DateTimeField(verbose_name = '最后一次操作时间', default = datetime.datetime.now)
  2404. meta = {
  2405. 'collection': 'permission_rule',
  2406. 'db_alias': 'default',
  2407. }
  2408. def get_permissionDict(self):
  2409. def update_permission(base_dict, update_dict):
  2410. result = {}
  2411. for key, value in base_dict.items():
  2412. if isinstance(value, dict):
  2413. res = update_permission(value, update_dict[key])
  2414. result[key] = res
  2415. else:
  2416. # print key,value,update_dict
  2417. if not isinstance(update_dict, dict):
  2418. result[key] = update_dict
  2419. else:
  2420. if key in update_dict:
  2421. result[key] = update_dict[key]
  2422. else:
  2423. result[key] = value
  2424. return result
  2425. return update_permission(PermissionInfo.get_permissions(), self.permissionDict)
  2426. def save(self, **kw):
  2427. self.lastModifiedTime = datetime.datetime.now()
  2428. return super(PermissionRule, self).save(**kw)
  2429. class TodoMessage(Searchable):
  2430. title = StringField(verbose_name=u"标题")
  2431. content = StringField(verbose_name=u"内容")
  2432. type = IntField(verbose_name=u"待办的种类", choices=TodoTypeEnum.choices())
  2433. link = StringField(verbose_name=u"跳转地址")
  2434. done = IntField(verbose_name=u"待办状态", choices=TodoDone.choices(), default=TodoDone.INIT)
  2435. ownerId = StringField(verbose_name=u"经销商")
  2436. expiredTime = DateTimeField(verbose_name=u"信息过期时间")
  2437. dateTimeAdded = DateTimeField(verbose_name=u"信息添加的时间", default=datetime.datetime.now())
  2438. popOnlyOnce = BooleanField(verbose_name=u"登录后台只显示一次", default=True)
  2439. def to_dict(self):
  2440. return {
  2441. "id": self.id,
  2442. "title": self.title,
  2443. 'content': self.content,
  2444. "type": self.type,
  2445. "link": self.link,
  2446. "status": self.done,
  2447. "ownerId": self.ownerId,
  2448. 'popOnlyOnce': self.popOnlyOnce
  2449. }
  2450. def has_done(self):
  2451. self.update(done=TodoDone.DONE)
  2452. @property
  2453. def checkModel(self):
  2454. for _ in TodoTypeEnum:
  2455. if _ == self.type:
  2456. return _._load_todoCls()
  2457. @classmethod
  2458. def get_todo_message(cls, user):
  2459. """
  2460. 获取所有未完成的任务
  2461. """
  2462. return cls.objects.filter(ownerId=str(user.id), done__in=[TodoDone.INIT, TodoDone.ING], expiredTime__gt=datetime.datetime.now())
  2463. @classmethod
  2464. def sim_expire_message(cls, ownerId, expireCount):
  2465. params = {
  2466. 'type': 'simCard',
  2467. 'redirect': concat_front_end_url(uri = '/app/deviceCard.html')
  2468. }
  2469. link = add_query(concat_server_end_url(uri = '/dealer/wechat/entry'), params)
  2470. msg = cls(
  2471. id = "1",
  2472. title = u"流量卡续费提醒",
  2473. content = u"您当前有{}台设备流量卡需要续费,为了避免影响设备的正常运行,请您尽快续费(已经充值请忽略)".format(expireCount),
  2474. type = TodoTypeEnum.SMS_TODO.code,
  2475. link = link,
  2476. ownerId = ownerId,
  2477. done = int(TodoDone.ING),
  2478. expiredTime = datetime.datetime.now() + datetime.timedelta(days = 365),
  2479. popOnlyOnce = False
  2480. ).to_dict()
  2481. return msg