models.py 94 KB

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