models.py 91 KB

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