models.py 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. """
  4. web.agent.models
  5. ~~~~~~~~~
  6. """
  7. import simplejson as json
  8. import datetime
  9. import itertools
  10. import logging
  11. import urllib
  12. from bson.objectid import ObjectId
  13. from django.conf import settings
  14. from django.utils.module_loading import import_string
  15. from mongoengine import MapField, LazyReferenceField, IntField
  16. from mongoengine.errors import DoesNotExist
  17. from mongoengine.fields import (StringField, DictField, BooleanField, DateTimeField, EmbeddedDocumentField,
  18. ListField, ReferenceField)
  19. from typing import Any, Dict, TYPE_CHECKING, Optional, cast
  20. from collections import namedtuple
  21. from apilib.monetary import RMB, sum_rmb, Permillage, Percent
  22. from apps.web.agent.define import AgentConst, AGENT_INCOME_SOURCE, AGENT_INCOME_TYPE
  23. from apps.web.agent.errors import PrimaryAgentDoesNotExist
  24. from apps.web.common.models import WithdrawRecord, CapitalUser, Balance
  25. from apps.web.constant import Const, AppPlatformType, DEALER_CONSUMPTION_AGG_KIND, MoniAppStatus
  26. from apps.web.core.db import Searchable, MonetaryField, StrictDictField, PermillageField, PercentField
  27. from apps.web.core.exceptions import InvalidParameter, NoAgentFound, NoManagerFound, MerchantError
  28. from apps.web.core.messages.sms import agentWithdrawSMSProvider
  29. from apps.web.core.models import WechatManagerApp, WechatAuthApp, BoundOpenInfo, AliApp, WechatPayApp, \
  30. WechatMiniApp, WechatUserManagerApp, JDAggrePayApp, JDAuthApp, BankCard, \
  31. WithdrawEntity, WechatUserSubscribeManagerApp, WechatDealerSubscribeManagerApp
  32. from apps.web.core import PayAppType, APP_KEY_DELIMITER, ROLE
  33. from apps.web.core.payment import WithdrawGateway
  34. from apps.web.device.models import DeviceType
  35. from apps.web.management.models import Manager
  36. from apps.web.common.transaction import WITHDRAW_PAY_TYPE
  37. from apps.web.exceptions import UserServerException
  38. logger = logging.getLogger(__name__)
  39. if TYPE_CHECKING:
  40. from pymongo.results import UpdateResult
  41. from apps.web.common.transaction import WithdrawHandler
  42. from apps.web.core import PayAppBase
  43. AgentDisclaimer = namedtuple("AgentDisclaimer", ["content", "version"])
  44. class Agent(CapitalUser):
  45. """
  46. 代理商, 管理经销商
  47. """
  48. INCOME_SOURCE_LIST = AGENT_INCOME_SOURCE.choices()
  49. INCOME_SOURCE_TO_TYPE = AgentConst.MAP_SOURCE_TO_TYPE
  50. INCOME_TYPE_LIST = AGENT_INCOME_TYPE.choices()
  51. INCOME_TYPE_TO_FIELD = AgentConst.MAP_TYPE_TO_FIELD
  52. #: 默认的收入频道的分布情况
  53. DEFAULT_INCOME_MAP = {
  54. AGENT_INCOME_SOURCE.AD: 0,
  55. AGENT_INCOME_SOURCE.DEALER_WITHDRAW_FEE: 0,
  56. AGENT_INCOME_SOURCE.DEALER_CARD_FEE: 0,
  57. AGENT_INCOME_SOURCE.DEALER_DEVICE_FEE: 0
  58. }
  59. #: 默认管理平台微信公众号授权用户信息
  60. DEFAULT_WECHAT_AUTH_USER_INFO = {
  61. 'avatar': '',
  62. 'sex': 0
  63. }
  64. DEFAULT_PAY_TYPE = {
  65. ROLE.agent: {
  66. AppPlatformType.ALIPAY: PayAppType.ALIPAY,
  67. AppPlatformType.WECHAT: PayAppType.WECHAT,
  68. AppPlatformType.JD: PayAppType.JD_AGGR
  69. },
  70. ROLE.dealer: {
  71. AppPlatformType.ALIPAY: PayAppType.ALIPAY,
  72. AppPlatformType.WECHAT: PayAppType.WECHAT,
  73. AppPlatformType.JD: PayAppType.JD_AGGR
  74. },
  75. ROLE.myuser: {
  76. AppPlatformType.ALIPAY: PayAppType.ALIPAY,
  77. AppPlatformType.WECHAT: PayAppType.WECHAT,
  78. AppPlatformType.JD: PayAppType.JD_AGGR,
  79. AppPlatformType.WECHAT_MINI: PayAppType.WECHAT_MINI
  80. }
  81. }
  82. DEFAULT_CHECKPOINT = {
  83. 'gerenzhongxin': False, 'yue': False, 'baogaolaoban': False, 'fukuan': False
  84. }
  85. # CAPITAL_PAY_APP_LIST = [WechatPayApp, JDAggrePayApp, JDOpenPayApp]
  86. #: 收入相关
  87. deviceBalance = MapField(field = EmbeddedDocumentField(Balance))
  88. trafficBalance = MapField(field = EmbeddedDocumentField(Balance))
  89. adBalance = MapField(field = EmbeddedDocumentField(Balance))
  90. withdrawBalance = MapField(field = EmbeddedDocumentField(Balance))
  91. insuranceBalance = MapField(field = EmbeddedDocumentField(Balance))
  92. apiQuotaBalance = MapField(field = EmbeddedDocumentField(Balance))
  93. disableAdBalance = MapField(field = EmbeddedDocumentField(Balance))
  94. incomeMap = DictField(verbose_name = '收入字典', default = DEFAULT_INCOME_MAP)
  95. aggregatedIncome = DictField(verbose_name = '累计收入,只增不减', default = DEFAULT_INCOME_MAP)
  96. #: 代理自定义自己的产品名称和logo
  97. productLogo = StringField(verbose_name = "产品logo", default = "")
  98. productName = StringField(verbose_name = "产品名称", default = "")
  99. #: 公众号相关
  100. gzhServiceQrcodeUrl = StringField(verbose_name = "公众号二维码", default = "", max_length = 256)
  101. gzhServiceLinkUrl = StringField(verbose_name = "公众号链接二维码", default = "")
  102. forceFollowGzh = BooleanField(verbose_name = "是否强制关注公众号", default = False)
  103. forceFollowGzhForDealer = StringField(verbose_name = u'经销商的强制关注开关',
  104. default = 'free') # never:此代理商下的经销商,永远不准强制关注,free:经销商自由允许关注
  105. title = StringField(verbose_name = u'公众号强制关注的时候,显示的title', default = u'为了充分保障您的支付权益,首次使用需要您关注服务公众号辅助使用设备。')
  106. desc = StringField(verbose_name = u'公众号的加粉描述', default = u'关注步骤:手指按在上面二维码上,弹出窗口后,点公众号关注后您再次扫描设备上二维码启动设备。')
  107. #: 用于客服的联系方式
  108. serviceName = StringField(verbose_name = "客服名称", default = "", max_length = 32)
  109. servicePhone = StringField(verbose_name = "客服电话", default = "", max_length = 32)
  110. serviceQrcodeUrl = StringField(verbose_name = "二维码图片链接", default = "", max_length = 256)
  111. #: 特性列表
  112. features = ListField(field = StringField(), verbose_name = u'支持的特性', default = [])
  113. customizedUserGzhAllowable = BooleanField(verbose_name = '用户自定义公众号', default = False)
  114. customizedUserSubGzhAllowable = BooleanField(verbose_name = '用户订阅通知自定义公众号', default = False)
  115. customizedDealerGzhAllowable = BooleanField(verbose_name = '经销商自定义公众号', default = False)
  116. customizedDealerSubGzhAllowable = BooleanField(verbose_name = '经销商订阅通知自定义公众号', default = False)
  117. managerId = StringField(verbose_name = "从属于哪个管理员", null = False)
  118. adShow = BooleanField(verbose_name = "广告收入显示与否的选项", default = False)
  119. # 厂商参与设备运营分成的商户比例 资金池模式
  120. managerProfitShare = PercentField(verbose_name='代理商设备运营分成比例', default = Percent('0.00'))
  121. # 厂商参与设备运营分成的商户比例 暂不支持,但是可以先预留
  122. managerMerProfitShare = PercentField(verbose_name='代理商设备运营分成比例 商户收款', default = Percent('0.00'))
  123. # 提现OPENID映射
  124. payOpenIdMap = MapField(EmbeddedDocumentField(BoundOpenInfo))
  125. #: 用于API接口
  126. openAPI = BooleanField(verbose_name = "是否支持openAPI", default = False)
  127. agentSign = StringField(verbose_name = '唯一签名用于API调用,agent的签名,用于核对对方的签名')
  128. mySign = StringField(verbose_name = '唯一签名用于API调用,我们的签名,用于请求对方URl,对方鉴权')
  129. domain = StringField(verbose_name = '域名调用URL')
  130. wechatLoginAuthApp = EmbeddedDocumentField(verbose_name='授权APP', document_type = WechatAuthApp)
  131. oldWechatLoginAuthApp = EmbeddedDocumentField(verbose_name = '老的授权APP', document_type = WechatAuthApp, default = None)
  132. wechatUserManagerialApp = EmbeddedDocumentField(verbose_name = '用户管理APP', document_type = WechatUserManagerApp)
  133. wechatDealerManagerialApp = EmbeddedDocumentField(verbose_name = '经销商管理APP', document_type = WechatManagerApp)
  134. wechatUserSubscribeManagerApp = EmbeddedDocumentField(verbose_name = '用户订阅通知APP',
  135. document_type = WechatUserSubscribeManagerApp)
  136. wechatDealerSubscribeManagerApp = EmbeddedDocumentField(verbose_name = '经销商订阅通知APP',
  137. document_type = WechatDealerSubscribeManagerApp)
  138. jdAuthApp = EmbeddedDocumentField(verbose_name = '管理APP', document_type = JDAuthApp, default = None)
  139. # 小程序实际上由前台绑定了, 设计需要改变
  140. # 小程序只能在主AGENT配置, 其他代理商不能配置任何资金池
  141. customizedWechatMiniAllowable = BooleanField(verbose_name = '自定义MINI', default = False)
  142. wechatMiniApp = EmbeddedDocumentField(verbose_name = u'微信小程序APP', document_type = WechatMiniApp, default = None)
  143. payAppWechatMini = LazyReferenceField(document_type = JDAggrePayApp, default = None)
  144. # payAppMini = GenericLazyReferenceField(choices = CAPITAL_PAY_APP_LIST, verbose_name = u'资金池APP', default = None)
  145. # 支付APP配置
  146. customizedAlipayCashflowAllowable = BooleanField(verbose_name = '是否开启自主支付宝收款权限', default = False)
  147. payAppAli = LazyReferenceField(document_type = AliApp, default = None)
  148. customizedWechatCashflowAllowable = BooleanField(verbose_name = '是否开启微信自主收款权限', default = False)
  149. payAppWechat = LazyReferenceField(document_type = WechatPayApp, verbose_name = '微信支付APP(用于提现和支付)', default = None)
  150. customizedJDAggreAllowable = BooleanField(verbose_name = '是否使用京东聚合支付', default = False)
  151. payAppJDAggre = LazyReferenceField(document_type = JDAggrePayApp, verbose_name = '京东聚合支付(仅用于支付)', default = None)
  152. bankcards = ListField(ReferenceField(BankCard), default = [])
  153. featureToggles = DictField(verbose_name = '特性开关', default = {})
  154. # 厂商给代理商的流量卡成本价. 提交经销商的时候,经销商的默认价格从这个继承,代理商也可以更改
  155. annualTrafficCost = MonetaryField(verbose_name = '厂商给代理商的流量卡成本价', default = Const.PLATFORM_DEFAULT_TRAFFIC_COST)
  156. # 平台给厂商的流量卡成本. 添加厂商的时候设置, 添加代理商的时候从厂商配置继承.
  157. # 如果有更改,更改之前的代理商不会修改这个费用,如果修改需要直接修改
  158. trafficCardCost = MonetaryField(verbose_name = '平台给厂商的流量卡成本价', default = Const.PLATFORM_DEFAULT_TRAFFIC_COST)
  159. # 厂商给代理商的提现费率. 代理商给经销商设置的提现费率必须大于该值. 配置资金池的代理商不受限制
  160. withdrawFeeRatio = PermillageField(verbose_name = '厂商给代理商的提现费率下限',
  161. default = Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO,
  162. min_value = Const.MIN_DEALER_WITHDRAW_FEE_RATIO,
  163. max_value = Const.MAX_DEALER_WITHDRAW_FEE_RATIO)
  164. # 平台给厂商的提现费率. 厂商给代理商的提现费率下限必须大于该值. 这个是平台成本价.
  165. withdrawFeeRatioCost = PermillageField(verbose_name = '平台给厂商的提现费率',
  166. default = Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO,
  167. min_value = Const.MIN_DEALER_WITHDRAW_FEE_RATIO,
  168. max_value = Const.MAX_DEALER_WITHDRAW_FEE_RATIO)
  169. bannerList = ListField(field = DictField(), verbose_name = "banner列表", default = [])
  170. #: 大部分情况下,是不允许直接变更支付网关的
  171. #: 手工给其转账的时候把该开关打开
  172. isChangingPaymentGateway = BooleanField(default = False)
  173. payType = DictField(verbose_name = u'各应用支付方式', default = DEFAULT_PAY_TYPE)
  174. moniAppList = ListField(verbose_name = u'监督公众号的清单', default = [])
  175. moniAppCheckPointDict = DictField(verbose_name = u'是否需要弹出监督公众号的代理商检查点', default = DEFAULT_CHECKPOINT)
  176. maxPayLimit = IntField(verbose_name = u'最大充值金额限制', default = 500)
  177. cardWechatInfo = DictField(verbose_name = u'批量导入实体卡绑定内勤微信账号信息', default = {})
  178. boundCardName = StringField(verbose_name = u'默认绑定卡主姓名', default = '')
  179. boundCardPhone = StringField(verbose_name = u'默认绑定卡主手机号', default = '')
  180. withdrawApps = MapField(EmbeddedDocumentField(document_type = WithdrawEntity), default = None)
  181. # 寻找 免责声明的 流程是 代理商--->主代理商--->系统代理商
  182. needDisclaimer = BooleanField(verbose_name = u"代理商是否需要免责声明", default = True)
  183. disclaimer = StringField(verbose_name = u"代理商设置的用户的免责声明", default = "")
  184. disclaimer_version = StringField(verbose_name = u"当前免责声明的版本,声明更新的时候须将版本号更新", default = "v1.0.0")
  185. dealerBankWithdrawFee = BooleanField(verbose_name = u'经销商提现到银行卡的手续费,计算方式', default = False)
  186. bankWithdrawFee = BooleanField(verbose_name = u"银行卡提现手续开关(资金池代理商开关和这个开关为双开关)", default = True)
  187. ledgerAppJDAggre = LazyReferenceField(document_type=JDAggrePayApp, verbose_name=u'京东聚合支付(仅用于设备收益分账)', default=None)
  188. meta = {
  189. 'indexes': [
  190. {
  191. 'fields': ['username'],
  192. 'unique': True
  193. }
  194. ],
  195. "collection": "Agent",
  196. "db_alias": "default"
  197. }
  198. search_fields = ('username', 'nickname', 'openId', 'remarks')
  199. def __str__(self):
  200. return 'Agent<id={} username={} nickname={}>'.format(str(self.id), self.username, self.nickname)
  201. @property
  202. def service_phone(self):
  203. return self.servicePhone
  204. def my_pay_type(self, role, gateway_type):
  205. if role in self.payType and gateway_type in self.payType[role]:
  206. return self.payType[role][gateway_type]
  207. else:
  208. return self.DEFAULT_PAY_TYPE[role][gateway_type]
  209. @property
  210. def my_wechat_pay_app(self):
  211. # type: ()->WechatPayApp
  212. if self.customizedWechatCashflowAllowable:
  213. if not self.payAppWechat:
  214. raise Exception(u'第三方支付配置错误(1001)')
  215. else:
  216. my_app = self.payAppWechat.fetch() # type: WechatPayApp
  217. if not my_app.valid:
  218. raise Exception(u'第三方支付配置错误(1002)')
  219. else:
  220. if (str(self.id) == settings.MY_PRIMARY_AGENT_ID) and (not my_app.inhouse):
  221. raise Exception(u'第三方支付配置错误(1003)')
  222. my_app.occupantId = str(self.id)
  223. my_app.occupant = self
  224. return my_app
  225. my_app = WechatPayApp.get_null_app()
  226. my_app.occupantId = str(self.id)
  227. my_app.occupant = self
  228. return my_app
  229. @property
  230. def my_ali_pay_app(self):
  231. # type: ()->AliApp
  232. if self.customizedAlipayCashflowAllowable:
  233. if not self.payAppAli:
  234. raise Exception(u'第三方支付配置错误(1001)')
  235. else:
  236. my_app = self.payAppAli.fetch() # type: AliApp
  237. if not my_app.valid:
  238. raise Exception(u'第三方支付配置错误(1002)')
  239. else:
  240. if (str(self.id) == settings.MY_PRIMARY_AGENT_ID) and (not my_app.inhouse):
  241. raise Exception(u'第三方支付配置错误(1003)')
  242. my_app.occupantId = str(self.id)
  243. my_app.occupant = self
  244. return my_app
  245. my_app = AliApp.get_null_app()
  246. my_app.occupantId = str(self.id)
  247. my_app.occupant = self
  248. return my_app
  249. @property
  250. def my_jd_aggre_pay_app(self):
  251. # type: ()->JDAggrePayApp
  252. if self.customizedJDAggreAllowable:
  253. if not self.payAppJDAggre:
  254. raise Exception(u'第三方支付配置错误(1001)')
  255. else:
  256. my_app = self.payAppJDAggre.fetch() # type: JDAggrePayApp
  257. if not my_app.valid:
  258. raise Exception(u'第三方支付配置错误(1002)')
  259. else:
  260. if (str(self.id) == settings.MY_PRIMARY_AGENT_ID) and (not my_app.inhouse):
  261. raise Exception(u'第三方支付配置错误(1003)')
  262. my_app.occupantId = str(self.id)
  263. my_app.occupant = self
  264. return my_app
  265. my_app = JDAggrePayApp.get_null_app()
  266. my_app.occupantId = str(self.id)
  267. my_app.occupant = self
  268. return my_app
  269. def to_dict(self, shadow = False):
  270. rv = super(Agent, self).to_dict()
  271. rv.update({
  272. 'id': str(self.id),
  273. 'productLogo': self.product_logo,
  274. 'productName': self.product_name,
  275. 'serviceName': self.serviceName,
  276. 'servicePhone': self.servicePhone,
  277. 'serviceQrcodeUrl': self.serviceQrcodeUrl,
  278. 'gzhServiceQrcodeUrl': self.gzhServiceQrcodeUrl,
  279. 'adShow': self.adShow,
  280. 'forceFollowGzh': self.forceFollowGzh,
  281. 'managerId': self.managerId,
  282. 'isPrimary': self.is_primary,
  283. 'featureList': self.feature_list,
  284. 'annualTrafficCost': self.annualTrafficCost,
  285. 'trafficCardCost': self.trafficCardCost,
  286. 'title': self.title,
  287. 'desc': self.desc,
  288. 'smsVendor': self.smsVendor,
  289. 'dealerBankWithdrawFee': self.dealerBankWithdrawFee,
  290. 'bankWithdrawFee': self.bankWithdrawFee
  291. })
  292. return rv
  293. @classmethod
  294. def filter(cls, manager_id, search_key, page_index, page_size, moniAppId = None, shadow = False):
  295. query = {'managerId': manager_id} if manager_id is not None else {}
  296. if moniAppId:
  297. query.update({'moniAppList': moniAppId})
  298. agents = cls.objects(**query).search(search_key).order_by('-dateTimeAdded')
  299. total = agents.count()
  300. dataList = []
  301. for agent in agents.paginate(pageIndex = page_index, pageSize = page_size): # type: Agent
  302. deviceTypes = DeviceType.objects(agentId = str(agent.id)).all()
  303. if len(deviceTypes):
  304. devTypeList = [_.to_dict() for _ in deviceTypes]
  305. else:
  306. devTypeList = []
  307. if Manager.objects(primeAgentId = str(agent.id)).first():
  308. isPrimary = True
  309. else:
  310. isPrimary = False
  311. zhejiangDict = {}
  312. try:
  313. from apps.web.south_intf.zhejiang_fire import ZhejiangNorther
  314. obj = ZhejiangNorther.objects.get(tokenId = str(agent.id))
  315. ip, port = obj.northPort.split(":")
  316. zhejiangDict = {
  317. 'loginUsername': obj.usernameFromHear,
  318. 'loginPassword': obj.passwordFromHear,
  319. 'mqUsername': obj.mqUser,
  320. 'mqPassword': obj.mqPassword,
  321. 'code': obj.serviceCodeFromNorth,
  322. "northIp": ip,
  323. "northPort": port
  324. }
  325. except Exception:
  326. pass
  327. item = {
  328. 'id': str(agent.id),
  329. 'nickname': agent.nickname,
  330. 'username': '******' if shadow else agent.username,
  331. 'annualTrafficCost': agent.annualTrafficCost,
  332. 'trafficCardCost': agent.trafficCardCost,
  333. 'withdrawFeeRatio': float(agent.withdrawFeeRatio.amount),
  334. 'withdrawFeeRatioCost': float(agent.withdrawFeeRatioCost.amount),
  335. 'managerProfitShare': float(agent.managerProfitShare.amount),
  336. 'dealerTotal': agent.get_dealers().count(),
  337. 'dateTimeAdded': agent.dateTimeAdded,
  338. 'featureList': agent.feature_list,
  339. 'bannerImgList': agent.bannerList,
  340. 'customizedAlipayCashflowAllowable': agent.customizedAlipayCashflowAllowable,
  341. 'customizedWechatCashflowAllowable': agent.customizedWechatCashflowAllowable,
  342. 'customizedDealerGzhAllowable': agent.customizedDealerGzhAllowable,
  343. 'customizedDealerSubGzhAllowable': agent.customizedDealerSubGzhAllowable,
  344. 'customizedUserGzhAllowable': agent.customizedUserGzhAllowable,
  345. 'customizedUserSubGzhAllowable': agent.customizedUserSubGzhAllowable,
  346. 'isPrimary': isPrimary,
  347. 'deviceType': devTypeList,
  348. 'detail': {
  349. 'agentId': str(agent.id),
  350. 'managerId': agent.managerId,
  351. 'features': json.dumps(agent.feature_boolean_map)
  352. },
  353. 'ZJFirePlatform': zhejiangDict,
  354. 'forceFollowGzh': 'yes' if agent.forceFollowGzh else 'no',
  355. 'forceFollowGzhForDealer': agent.forceFollowGzhForDealer,
  356. 'productName': agent.productName,
  357. 'maxPayLimit': agent.maxPayLimit
  358. }
  359. try:
  360. pay_app_ali = agent.my_ali_pay_app # type: Optional[AliApp]
  361. except Exception as e:
  362. pay_app_ali = None
  363. if pay_app_ali: # type
  364. item.update({'aliPayApp': pay_app_ali.to_dict()}) # type: AliApp
  365. else:
  366. item.update({'aliPayApp': AliApp.get_null_app().to_dict()})
  367. try:
  368. pay_app_wechat = agent.my_wechat_pay_app
  369. except Exception as e:
  370. pay_app_wechat = None
  371. if pay_app_wechat:
  372. item.update({'wechatPayApp': pay_app_wechat.to_dict()})
  373. else:
  374. item.update({'wechatPayApp': WechatPayApp.get_null_app().to_dict()})
  375. dealer_manager_app = agent.wechatDealerManagerialApp or WechatManagerApp.get_null_app() # type: WechatManagerApp
  376. item.update({'dealer': dealer_manager_app.to_dict()})
  377. user_manager_app = agent.wechatUserManagerialApp or WechatUserManagerApp.get_null_app() # type: WechatUserManagerApp
  378. item.update({'user': user_manager_app.to_dict()})
  379. user_sub_manager_app = agent.wechatUserSubscribeManagerApp or WechatUserSubscribeManagerApp.get_null_app() # type: WechatUserSubscribeManagerApp
  380. item.update({'user_sub': user_sub_manager_app.to_dict()})
  381. dealer_sub_manager_app = agent.wechatDealerSubscribeManagerApp or WechatDealerSubscribeManagerApp.get_null_app() # type: WechatDealerSubscribeManagerApp
  382. item.update({'dealer_sub': dealer_sub_manager_app.to_dict()})
  383. # 监督号的配置
  384. moniAppList = []
  385. for appId in agent.moniAppList:
  386. try:
  387. moniApp = MoniApp.objects.get(appId = appId)
  388. except Exception, e:
  389. continue
  390. moniAppList.append({'appId': moniApp.appId, 'appName': moniApp.appName})
  391. item.update({'moniApps': moniAppList})
  392. pointList = []
  393. for key, switch in agent.moniAppCheckPointDict.items():
  394. try:
  395. point = MoniAppPoint.objects.get(key = key)
  396. except Exception, e:
  397. continue
  398. pointList.append({'name': point.name, 'key': key, 'switch': switch})
  399. item.update({'pointDict': pointList})
  400. if agent.is_primary:
  401. item.update({'isPrimary': True})
  402. dataList.insert(0, item)
  403. else:
  404. item.update({'isPrimary': False})
  405. dataList.append(item)
  406. return total, dataList
  407. @property
  408. def product_logo(self):
  409. if not self.productLogo:
  410. return self.primary_agent.productLogo
  411. else:
  412. return self.productLogo
  413. @property
  414. def product_name(self):
  415. if not self.productName:
  416. return self.primary_agent.productName
  417. else:
  418. return self.productName
  419. @staticmethod
  420. def get_agent(agentId):
  421. try:
  422. agent = Agent.objects.get(id = ObjectId(agentId)) # type: Agent
  423. return agent.to_dict()
  424. except DoesNotExist:
  425. logger.exception('can not find agent from db,id=%s' % agentId)
  426. return None
  427. def update(self, **kwargs):
  428. # type: (**Any)->int
  429. """
  430. 每次更新的时候确保正在更改支付网关的选项设置为关闭
  431. :param kwargs:
  432. :return:
  433. """
  434. return super(Agent, self).update(**kwargs)
  435. @staticmethod
  436. def update_agent(agentId, valueDict):
  437. """TODO REFACTOR"""
  438. if not valueDict:
  439. return True
  440. try:
  441. agent = Agent.objects(id = agentId).get()
  442. updated = agent.update(**valueDict)
  443. assert updated, u'更新失败'
  444. except Exception as e:
  445. logger.exception('update agent error=%s' % e)
  446. return False
  447. return True
  448. @staticmethod
  449. def record_cookie(agentId, response):
  450. agent = Agent.get_agent(agentId)
  451. if agent is None:
  452. return response
  453. response.set_cookie(key = 'agentLogoUrl',
  454. value = agent['productLogo'],
  455. max_age = 24 * 3600 * 30,
  456. domain = settings.COOKIE_DOMAIN)
  457. pn = urllib.quote(agent['productName'].encode('utf-8'))
  458. response.set_cookie(key = 'agentBrandName', value = pn, max_age = 24 * 3600 * 30,
  459. domain = settings.COOKIE_DOMAIN)
  460. response.set_cookie(key = 'agentId', value = agentId, max_age = 24 * 3600 * 30, domain = settings.COOKIE_DOMAIN)
  461. return response
  462. def put_cookie(self, response):
  463. logo = self.product_logo
  464. if not logo:
  465. logo = '/app/img/logo.png'
  466. response.set_cookie(key = 'agentLogoUrl', value = logo,
  467. max_age = 24 * 3600 * 30,
  468. domain = settings.COOKIE_DOMAIN)
  469. productName = self.product_name
  470. response.set_cookie(key = 'agentBrandName',
  471. value = urllib.quote(productName.encode('utf-8')),
  472. max_age = 24 * 3600 * 30,
  473. domain = settings.COOKIE_DOMAIN)
  474. response.set_cookie(key = 'agentId',
  475. value = str(self.id),
  476. max_age = 24 * 3600 * 30,
  477. domain = settings.COOKIE_DOMAIN)
  478. return response
  479. def get_dealers(self):
  480. """
  481. 获取旗下经销商
  482. :return:
  483. """
  484. Dealer = import_string('apps.web.dealer.models.Dealer')
  485. return Dealer.objects(agentId = str(self.id))
  486. @property
  487. def primary_agent_id(self):
  488. return self.manager.primeAgentId
  489. @property
  490. def manager(self): # type:() -> Manager
  491. if not hasattr(self, '__manager__'):
  492. manager = Manager.objects(id=self.managerId).first()
  493. if not manager:
  494. raise NoManagerFound(manager_id=self.managerId)
  495. setattr(self, '__manager__', manager)
  496. return getattr(self, '__manager__')
  497. @property
  498. def primary_agent(self): # type:() -> Agent
  499. primeAgent = Agent.objects(id=self.primary_agent_id).first()
  500. if not primeAgent:
  501. raise NoAgentFound(agent_id=self.primary_agent_id)
  502. setattr(self, '__my_prime_agent__', primeAgent)
  503. return getattr(self, '__my_prime_agent__')
  504. @property
  505. def is_primary(self):
  506. """
  507. 不需要去多查询一次数据库 只需要找到manager即可
  508. """
  509. return self.is_equal(self.primary_agent)
  510. @property
  511. def is_in_domain(self):
  512. try:
  513. return self.manager.domain == settings.MY_DOMAIN
  514. except Exception as e:
  515. logger.error('get manager failure. error = %s; id = %s' % (str(e), str(self.id)))
  516. return False
  517. @classmethod
  518. def from_agent(cls, agent, app_type, **kwargs):
  519. param_key = '{app_type}_app'.format(app_type = app_type)
  520. attr_or_func = getattr(agent, param_key)
  521. if hasattr(attr_or_func, '__call__'):
  522. return attr_or_func(**kwargs)
  523. else:
  524. return attr_or_func
  525. @classmethod
  526. def factory(cls, **kwargs):
  527. factory_type = kwargs.pop('factory_type')
  528. if factory_type == 'app':
  529. app_type = kwargs.pop('app_type')
  530. return lambda agent: cls.from_agent(agent = agent, app_type = app_type, **kwargs)
  531. elif factory_type == 'withdraw_source_key':
  532. pay_app = kwargs.pop('pay_app')
  533. return lambda agent: getattr(agent, 'withdraw_source_key')(pay_app)
  534. else:
  535. raise InvalidParameter(u'参数错误')
  536. @staticmethod
  537. def get_inhouse_prime_agent():
  538. # type: ()->Agent
  539. """
  540. 为了方便识别,如果获取默认代理商失败,需要报错为默认代理商找不到
  541. """
  542. try:
  543. return Agent.objects(id = str(settings.MY_PRIMARY_AGENT_ID)).get()
  544. except DoesNotExist:
  545. raise PrimaryAgentDoesNotExist('failed to get default primary agent')
  546. @property
  547. def inhouse_prime_agent(self):
  548. if str(self.id) == str(settings.MY_PRIMARY_AGENT_ID):
  549. return self
  550. else:
  551. return Agent.get_inhouse_prime_agent()
  552. @property
  553. def customizedCashflowAllowable(self):
  554. """
  555. 目前withdrawSourceKey是使用微信商户来标记. 所以资金池场景下仅
  556. 判断微信商户是否支持就可以。在资金池场景下, 必须配置微信支付
  557. :return:
  558. """
  559. return self.customizedWechatCashflowAllowable
  560. def wechat_mini_env_pay_app(self, role = None, pay_app_type = None):
  561. # type: (str, str)->WechatMiniApp
  562. if not self.customizedWechatMiniAllowable:
  563. primary_agent = self.primary_agent
  564. if self.is_equal(primary_agent):
  565. return self.inhouse_prime_agent.wechat_mini_env_pay_app(role, pay_app_type)
  566. else:
  567. return primary_agent.wechat_mini_env_pay_app(role, pay_app_type)
  568. if self.customizedWechatMiniAllowable:
  569. if not self.customizedCashflowAllowable:
  570. raise Exception(u'第三方支付配置错误(1001)')
  571. app = self.payAppWechatMini.fetch()
  572. if app and app.enable and app.valid:
  573. app.occupantId = str(self.id)
  574. app.occupant = self
  575. return app
  576. app = self.wechatMiniApp # type: WechatMiniApp
  577. if app and app.enable and app.valid:
  578. app.occupantId = str(self.id)
  579. app.occupant = self
  580. return app
  581. raise Exception(u'第三方支付配置错误(1002)')
  582. else:
  583. return WechatMiniApp.get_null_app()
  584. def wechat_env_pay_app(self, role = None, pay_app_type = None):
  585. # type: (Optional[None, str], Optional[None, str])->Optional[cast(PayAppBase)]
  586. assert not (role and pay_app_type), 'role and app_type must not have value in the same time.'
  587. if not self.customizedCashflowAllowable:
  588. if pay_app_type:
  589. _pay_app_type = pay_app_type
  590. _role = None
  591. else:
  592. custom = self.payType.get('custom', False)
  593. if custom:
  594. if role in self.payType and AppPlatformType.WECHAT in self.payType[role]:
  595. _pay_app_type = self.payType[role][AppPlatformType.WECHAT]
  596. _role = None
  597. else:
  598. _pay_app_type = None
  599. _role = role
  600. else:
  601. _pay_app_type = None
  602. _role = role
  603. primary_agent = self.primary_agent
  604. if self.is_equal(primary_agent):
  605. return self.inhouse_prime_agent.wechat_env_pay_app(_role, _pay_app_type)
  606. else:
  607. return primary_agent.wechat_env_pay_app(_role, _pay_app_type)
  608. if role:
  609. app_pay_type = self.my_pay_type(role, AppPlatformType.WECHAT)
  610. else:
  611. app_pay_type = pay_app_type
  612. if app_pay_type == PayAppType.WECHAT:
  613. app = self.my_wechat_pay_app # type: WechatPayApp
  614. if not app.enable or not app.valid:
  615. raise Exception(u'系统配置错误(第三方支付)')
  616. return app
  617. if app_pay_type == PayAppType.JD_AGGR:
  618. app = self.my_jd_aggre_pay_app # type: JDAggrePayApp
  619. if not app.enable or not app.valid:
  620. raise Exception(u'系统配置错误(第三方支付)')
  621. return app
  622. raise Exception(u'系统配置错误(第三方支付)')
  623. def alipay_env_pay_app(self, role = None, pay_app_type = None):
  624. assert not (role and pay_app_type), 'role and app_type must not have value in the same time.'
  625. if not self.customizedCashflowAllowable:
  626. # 如果代理商配置了需要的支付类型, 以代理商配置的为准
  627. if pay_app_type:
  628. _pay_app_type = pay_app_type
  629. _role = None
  630. else:
  631. custom = self.payType.get('custom', False)
  632. if custom:
  633. if role in self.payType and AppPlatformType.ALIPAY in self.payType[role]:
  634. _pay_app_type = self.payType[role][AppPlatformType.ALIPAY]
  635. _role = None
  636. else:
  637. _pay_app_type = None
  638. _role = role
  639. else:
  640. _pay_app_type = None
  641. _role = role
  642. primary_agent = self.primary_agent
  643. if self.is_equal(primary_agent):
  644. return self.inhouse_prime_agent.alipay_env_pay_app(_role, _pay_app_type)
  645. else:
  646. return primary_agent.alipay_env_pay_app(_role, _pay_app_type)
  647. if role:
  648. app_pay_type = self.my_pay_type(role, AppPlatformType.ALIPAY)
  649. else:
  650. app_pay_type = pay_app_type
  651. if app_pay_type == PayAppType.ALIPAY:
  652. app = self.my_ali_pay_app # type: AliApp
  653. if not app.enable or not app.valid:
  654. raise Exception(u'系统配置错误(第三方支付)')
  655. return app
  656. if app_pay_type == PayAppType.JD_AGGR:
  657. app = self.my_jd_aggre_pay_app # type: JDAggrePayApp
  658. if not app.enable or not app.valid:
  659. raise Exception(u'系统配置错误(第三方支付)')
  660. return app
  661. raise Exception(u'系统配置错误(第三方支付)')
  662. def jd_env_pay_app(self, role = None, pay_app_type = None):
  663. assert not (role and pay_app_type), 'role and app_type must not have value in the same time.'
  664. if not self.customizedCashflowAllowable:
  665. if pay_app_type:
  666. _pay_app_type = pay_app_type
  667. _role = None
  668. else:
  669. custom = self.payType.get('custom', False)
  670. if custom:
  671. if role in self.payType and AppPlatformType.JD in self.payType[role]:
  672. _pay_app_type = self.payType[role][AppPlatformType.JD]
  673. _role = None
  674. else:
  675. _pay_app_type = None
  676. _role = role
  677. else:
  678. _pay_app_type = None
  679. _role = role
  680. primary_agent = self.primary_agent
  681. if self.is_equal(primary_agent):
  682. return self.inhouse_prime_agent.jd_env_pay_app(_role, _pay_app_type)
  683. else:
  684. return primary_agent.jd_env_pay_app(_role, _pay_app_type)
  685. if role:
  686. app_pay_type = self.my_pay_type(role, AppPlatformType.JD)
  687. else:
  688. app_pay_type = pay_app_type
  689. if app_pay_type == PayAppType.JD_AGGR:
  690. app = self.my_jd_aggre_pay_app # type: JDAggrePayApp
  691. if not app.enable or not app.valid:
  692. raise Exception(u'系统配置错误(第三方支付)')
  693. return app
  694. raise Exception(u'系统配置错误(第三方支付)')
  695. def _check_wechat_withdraw(self):
  696. my_app = self.my_wechat_pay_app
  697. if not my_app.enable:
  698. raise Exception(u'系统配置错误(三方支付)')
  699. def withdraw_source_key(self, pay_app = None):
  700. # type:(PayAppBase)->str
  701. if not pay_app:
  702. my_app = self.wechat_env_pay_app(pay_app_type = PayAppType.WECHAT)
  703. if (not my_app) or (not my_app.valid) or (not my_app.enable):
  704. raise Exception(u'配置错误(第三方支付)')
  705. else:
  706. agent = pay_app.occupant # type: Agent
  707. my_app = agent.my_wechat_pay_app # type: WechatPayApp
  708. if (not my_app) or (not my_app.valid) or (not my_app.enable):
  709. raise Exception(u'配置错误(第三方支付)')
  710. if hasattr(my_app, '__source_key__'):
  711. return APP_KEY_DELIMITER.join(
  712. [WithdrawGateway.LEDGER_PREFIX, my_app.pay_app_type, getattr(my_app, '__source_key__')])
  713. else:
  714. raise AttributeError('no __source_key__ attribute')
  715. # if my_app.inhouse:
  716. # return settings.MY_PRIMARY_AGENT_WALLET_KEY # 所有平台inhouse资金池全部使用固定的资金池KEY
  717. # else:
  718. # return APP_KEY_DELIMITER.join(
  719. # [WithdrawGateway.LEDGER_PREFIX, my_app.pay_app_type, getattr(my_app, '__source_key__')])
  720. @classmethod
  721. def _parse_source_key(cls, source_key):
  722. # type: (str)->tuple
  723. tokens = source_key.split(APP_KEY_DELIMITER)
  724. return True if tokens[0].startswith(WithdrawGateway.LEDGER_PREFIX) else False, tokens[1], tokens[2], tokens[3:]
  725. # if source_key == settings.MY_PRIMARY_AGENT_WALLET_KEY:
  726. # is_ledger, pay_app_type, occupant_id = True, PayAppType.WECHAT, settings.MY_PRIMARY_AGENT_WALLET_KEY
  727. # else:
  728. # tokens = source_key.split(APP_KEY_DELIMITER)
  729. # is_ledger, pay_app_type, occupant_id = True if tokens[0].startswith(
  730. # WithdrawGateway.LEDGER_PREFIX) else False, tokens[1], tokens[2]
  731. @property
  732. def disclaimerVersionFormat(self):
  733. return "{}_{}"
  734. @property
  735. def disclaimerVersion(self):
  736. verFormat = self.disclaimerVersionFormat
  737. return verFormat.format(str(self.id), self.disclaimer_version)
  738. @disclaimerVersion.setter
  739. def disclaimerVersion(self, value):
  740. try:
  741. self.update(disclaimer_version = value)
  742. except Exception as e:
  743. logger.error(
  744. "set disclaimer version error, agent is <{}>, version is <{}>, error is ".format(self, value, e))
  745. raise e
  746. @property
  747. def disclaimerContent(self):
  748. return self.disclaimer
  749. @disclaimerContent.setter
  750. def disclaimerContent(self, value):
  751. try:
  752. self.update(disclaimer = value)
  753. except Exception as e:
  754. logger.error("set disclaimer content error, agent is <{}>. error is <{}>".format(self, e))
  755. raise e
  756. @property
  757. def disclaimerAgent(self):
  758. """
  759. 获取设置了免责声明的代理商
  760. :return:
  761. """
  762. if self.disclaimer:
  763. return self
  764. primeAgent = self.primary_agent
  765. if primeAgent.disclaimer:
  766. return primeAgent
  767. inhousePrimeAgent = self.inhouse_prime_agent
  768. return inhousePrimeAgent
  769. @classmethod
  770. def get_disclaimer(cls, agentId):
  771. """
  772. 获取 代理商的 免责声明
  773. :param agentId:
  774. :return:
  775. """
  776. agent = cls.objects.get(id = agentId)
  777. if not agent.needDisclaimer:
  778. return AgentDisclaimer(content = "", version = agent.disclaimerVersion)
  779. agent = agent.disclaimerAgent
  780. content = agent.disclaimerContent
  781. disclaimer = AgentDisclaimer(content = content, version = agent.disclaimerVersion)
  782. return disclaimer
  783. @classmethod
  784. def withdraw_gateway_list(cls, source_key):
  785. is_ledger, pay_app_type, occupant_id, tokens = cls._parse_source_key(source_key)
  786. if not is_ledger:
  787. return {
  788. 'alipay': WithdrawGateway(AliApp.get_null_app(), False),
  789. 'wechat': WithdrawGateway(WechatPayApp.get_null_app(), False)
  790. }
  791. if pay_app_type != PayAppType.WECHAT:
  792. raise UserServerException(u'系统配置错误(第三方支付),请联系平台客服(1001)')
  793. agent = cls.objects(id = occupant_id).first()
  794. if not agent:
  795. raise UserServerException(u'系统配置错误(第三方支付),请联系平台客服(1002)')
  796. if not agent.withdrawApps:
  797. return {
  798. 'alipay': None,
  799. 'wechat': agent.my_wechat_pay_app.new_withdraw_gateway(is_ledger = is_ledger)}
  800. else:
  801. if source_key not in agent.withdrawApps:
  802. raise UserServerException(u'系统配置错误(第三方支付),请联系平台客服(1003)')
  803. withdraw_entity = agent.withdrawApps[source_key] # type: WithdrawEntity
  804. withdraw_entity.alipay_app.occupantId = str(agent.id)
  805. withdraw_entity.alipay_app.occupant = agent
  806. withdraw_entity.wechat_app.occupantId = str(agent.id)
  807. withdraw_entity.wechat_app.occupant = agent
  808. return {
  809. 'alipay': withdraw_entity.alipay_app.new_withdraw_gateway(is_ledger = is_ledger),
  810. 'wechat': withdraw_entity.wechat_app.new_withdraw_gateway(is_ledger = is_ledger)
  811. }
  812. @staticmethod
  813. def get_platform_wechat_manager_app():
  814. agent = Agent.get_inhouse_prime_agent()
  815. return agent.platform_wechat_manager_app
  816. @property
  817. def platform_wechat_manager_app(self):
  818. agent = self.inhouse_prime_agent
  819. app = agent.wechatDealerManagerialApp
  820. if not app:
  821. raise Exception(u'公众号配置错误')
  822. app.occupantId = str(agent.id)
  823. return app
  824. @staticmethod
  825. def get_inhouse_wechat_user_manager_app():
  826. agent = Agent.get_inhouse_prime_agent()
  827. return agent.inhouse_wechat_user_manager_app
  828. @property
  829. def inhouse_wechat_user_manager_app(self):
  830. agent = self.inhouse_prime_agent
  831. app = agent.wechatUserManagerialApp
  832. if not app:
  833. raise Exception(u'公众号配置错误')
  834. app.occupantId = str(agent.id)
  835. return app
  836. @property
  837. def wechat_auth_app(self):
  838. """
  839. 标识用户只用平台的公众号对应APPID. 部分用户由于前期原因配置了自己的
  840. 该接口仅做兼容
  841. :return:
  842. """
  843. if self.wechatLoginAuthApp:
  844. app = self.wechatLoginAuthApp # type: WechatAuthApp
  845. app.occupantId = str(self.id)
  846. return app
  847. else:
  848. primary_agent = self.primary_agent
  849. if self.is_equal(primary_agent):
  850. return self.inhouse_prime_agent.wechat_auth_app
  851. else:
  852. return primary_agent.wechat_auth_app
  853. @property
  854. def wechat_user_manager_app(self):
  855. if self.customizedUserGzhAllowable:
  856. if self.wechatUserManagerialApp:
  857. app = self.wechatUserManagerialApp # type: WechatManagerApp
  858. app.occupantId = str(self.id)
  859. app.occupant = self
  860. return app
  861. else:
  862. raise Exception(u'系统配置错误(wechat_user_manager_app)')
  863. else:
  864. primary_agent = self.primary_agent
  865. if self.is_equal(primary_agent):
  866. inhouse_agent = self.inhouse_prime_agent
  867. if inhouse_agent.customizedUserGzhAllowable:
  868. return inhouse_agent.wechat_user_manager_app
  869. else:
  870. raise Exception(u'系统未配置(wechat_user_manager_app)')
  871. else:
  872. return primary_agent.wechat_user_manager_app
  873. @property
  874. def wechat_user_messager_app(self):
  875. if self.customizedUserGzhAllowable or self.customizedUserSubGzhAllowable:
  876. if self.customizedUserGzhAllowable:
  877. if self.wechatUserManagerialApp:
  878. app = self.wechatUserManagerialApp # type: WechatUserManagerApp
  879. app.occupantId = str(self.id)
  880. app.occupant = self
  881. return app
  882. else:
  883. raise Exception(u'系统配置错误(wechat_user_manager_app)')
  884. if self.customizedUserSubGzhAllowable:
  885. if self.wechatUserSubscribeManagerApp:
  886. app = self.wechatUserSubscribeManagerApp # type: WechatUserSubscribeManagerApp
  887. app.occupantId = str(self.id)
  888. app.occupant = self
  889. return app
  890. else:
  891. raise Exception(u'系统配置错误(wechat_user_subscribe_manager_app)')
  892. else:
  893. primary_agent = self.primary_agent
  894. if self.is_equal(primary_agent):
  895. inhouse_agent = self.inhouse_prime_agent
  896. if inhouse_agent.customizedUserGzhAllowable or inhouse_agent.customizedUserSubGzhAllowable:
  897. return inhouse_agent.wechat_user_messager_app
  898. else:
  899. raise Exception(u'系统未配置(wechat_user_manager_app|wechat_user_subscribe_manager_app)')
  900. else:
  901. return primary_agent.wechat_user_messager_app
  902. @property
  903. def wechat_manager_app(self):
  904. if self.customizedDealerGzhAllowable:
  905. if self.wechatDealerManagerialApp:
  906. app = self.wechatDealerManagerialApp # type: WechatManagerApp
  907. app.occupantId = str(self.id)
  908. return app
  909. else:
  910. raise Exception(u'系统配置错误(wechat_dealer_manager_app, %s)' % str(self.id))
  911. else:
  912. primary_agent = self.primary_agent
  913. if self.is_equal(primary_agent):
  914. return self.inhouse_prime_agent.wechat_manager_app
  915. else:
  916. return primary_agent.wechat_manager_app
  917. @property
  918. def wechat_user_subscribe_manager_app(self):
  919. if self.customizedUserSubGzhAllowable:
  920. if self.wechatUserSubscribeManagerApp:
  921. app = self.wechatUserSubscribeManagerApp # type: WechatManagerApp
  922. app.occupantId = str(self.id)
  923. app.occupant = self
  924. return app
  925. else:
  926. raise Exception(u'系统配置错误(wechat_user_subscribe_manager_app)')
  927. else:
  928. primary_agent = self.primary_agent
  929. if self.is_equal(primary_agent):
  930. inhouse_agent = self.inhouse_prime_agent
  931. if inhouse_agent.customizedUserSubGzhAllowable:
  932. return inhouse_agent.wechat_user_subscribe_manager_app
  933. else:
  934. raise Exception(u'系统未配置(wechat_user_subscribe_manager_app)')
  935. else:
  936. return primary_agent.wechat_user_subscribe_manager_app
  937. @property
  938. def wechat_dealer_subscribe_manager_app(self):
  939. if self.customizedDealerSubGzhAllowable:
  940. if self.wechatDealerSubscribeManagerApp:
  941. app = self.wechatDealerSubscribeManagerApp # type: WechatManagerApp
  942. app.occupantId = str(self.id)
  943. app.occupant = self
  944. return app
  945. else:
  946. raise Exception(u'系统配置错误(wechat_user_manager_app)')
  947. else:
  948. primary_agent = self.primary_agent
  949. if self.is_equal(primary_agent):
  950. return self.inhouse_prime_agent.wechatDealerSubscribeManagerApp
  951. else:
  952. return primary_agent.wechatDealerSubscribeManagerApp
  953. def get_user_sub_template_id_list(self):
  954. if not self.customizedUserSubGzhAllowable:
  955. return []
  956. if not self.wechatUserSubscribeManagerApp.templateIdMap:
  957. return []
  958. else:
  959. try:
  960. return map(lambda _: _['templateId'], self.wechatUserSubscribeManagerApp.templateIdMap.values())
  961. except Exception as e:
  962. logger.info('error e=<{}>'.format(e))
  963. return []
  964. def get_dealer_sub_template_id_list(self):
  965. if not self.customizedDealerSubGzhAllowable:
  966. return []
  967. if not self.wechatUserSubscribeManagerApp.templateIdMap:
  968. return []
  969. else:
  970. try:
  971. return map(lambda _: _['templateId'], self.wechatDealerSubscribeManagerApp.templateIdMap.values())
  972. except Exception as e:
  973. logger.info('error e=<{}>'.format(e))
  974. return []
  975. @property
  976. def alipay_auth_app(self):
  977. if not self.customizedAlipayCashflowAllowable:
  978. primary_agent = self.primary_agent
  979. if self.is_equal(primary_agent):
  980. return self.inhouse_prime_agent.alipay_auth_app
  981. else:
  982. return primary_agent.alipay_auth_app
  983. app = self.my_ali_pay_app # type: AliApp
  984. logger.debug("app id is:{}".format(str(app.id)))
  985. if not app.valid:
  986. raise Exception(u'系统配置错误(第三方支付)')
  987. app.occupantId = str(self.id)
  988. return app
  989. @property
  990. def jd_auth_app(self):
  991. """
  992. 标识用户只用平台的公众号对应APPID. 部分用户由于前期原因配置了自己的
  993. 该接口仅做兼容
  994. :return:
  995. """
  996. if self.jdAuthApp:
  997. app = self.jdAuthApp # type: JDAuthApp
  998. app.occupantId = str(self.id)
  999. return app
  1000. else:
  1001. primary_agent = self.primary_agent
  1002. if self.is_equal(primary_agent):
  1003. return self.inhouse_prime_agent.jdAuthApp
  1004. else:
  1005. return primary_agent.jdAuthApp
  1006. @property
  1007. def wechat_mini_auth_app(self):
  1008. if not self.customizedWechatMiniAllowable:
  1009. primary_agent = self.primary_agent
  1010. if self.is_equal(primary_agent):
  1011. return self.inhouse_prime_agent.wechat_mini_auth_app
  1012. else:
  1013. return primary_agent.wechat_mini_auth_app
  1014. app = self.wechatMiniApp
  1015. if not app.valid:
  1016. raise Exception(u'系统配置错误(第三方支付)')
  1017. app.occupantId = str(self.id)
  1018. return app
  1019. def income_by_date(self, date, specific_source = None):
  1020. """
  1021. :param date:
  1022. :param specific_source:
  1023. :return:
  1024. """
  1025. reports = AgentIncomeReport.objects(agentId = str(self.id), date = date)
  1026. if specific_source:
  1027. reports = reports.filter(source = specific_source)
  1028. return RMB(reports.sum('amount'))
  1029. def today_income(self, specific_source = None):
  1030. today = datetime.datetime.now().strftime(Const.DATE_FMT)
  1031. return self.income_by_date(date = today, specific_source = specific_source)
  1032. def yesterday_income(self, specific_source = None):
  1033. yesterday = (datetime.datetime.now() - datetime.timedelta(days = 1)).strftime(Const.DATE_FMT)
  1034. return self.income_by_date(date = yesterday, specific_source = specific_source)
  1035. def aggregate_income(self, specific_source = None):
  1036. """
  1037. 获取聚合收入
  1038. :param specific_source: 特定source
  1039. :return:
  1040. """
  1041. if not specific_source:
  1042. return sum_rmb(self.aggregatedIncome.values())
  1043. else:
  1044. return sum_rmb([v for k, v in self.aggregatedIncome.items() if k == specific_source])
  1045. def _single_month_income(self, year, month, specific_source):
  1046. if not specific_source:
  1047. reports = AgentIncomeReport.objects(agentId = str(self.id), date__startswith = '%d-%02d' % (year, month))
  1048. else:
  1049. reports = AgentIncomeReport.objects(agentId = str(self.id), date__startswith = '%d-%02d' % (year, month),
  1050. source = specific_source)
  1051. return RMB(reports.sum('amount'))
  1052. def current_month_income(self, specific_source = None):
  1053. now = datetime.datetime.now()
  1054. return self._single_month_income(now.year, now.month, specific_source)
  1055. def monthly_income(self, specific_source = None):
  1056. """
  1057. 获得月报表
  1058. :param specific_source:
  1059. :return:
  1060. """
  1061. now = datetime.datetime.now()
  1062. years = {_ for _ in range(2017, now.year + 1)}
  1063. months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
  1064. return {(year, month): self._single_month_income(year, month, specific_source)
  1065. for year, month in itertools.product(years, months)}
  1066. def is_equal(self, agent):
  1067. return str(self.id) == str(agent.id)
  1068. @property
  1069. def supported_device_types(self):
  1070. result = DeviceType.all(str(self.id))
  1071. if result:
  1072. return result
  1073. primary_agent = self.primary_agent
  1074. if not self.is_equal(primary_agent):
  1075. return primary_agent.supported_device_types
  1076. else:
  1077. return []
  1078. @property
  1079. def feature_boolean_map(self):
  1080. # type: ()->Dict[str, bool]
  1081. features = super(Agent, self).feature_boolean_map
  1082. if self.customizedWechatCashflowAllowable:
  1083. features.update({
  1084. 'show_withdraw_management': True
  1085. })
  1086. return features
  1087. @property
  1088. def hide_consume_kinds_dealer(self):
  1089. rv = []
  1090. if 'hiddenUsedTime' in self.features:
  1091. rv.append(DEALER_CONSUMPTION_AGG_KIND.DURATION)
  1092. if 'hidden_coins' in self.features:
  1093. rv.append(DEALER_CONSUMPTION_AGG_KIND.COIN)
  1094. left_features = set(self.features) - {'hiddenUsedTime', 'hidden_coins'}
  1095. for feature in left_features:
  1096. if feature.startswith('hide_') and feature.endswith('_for_dealer'):
  1097. kind = feature.replace('hide_', '').replace('_for_dealer', '')
  1098. if kind in DEALER_CONSUMPTION_AGG_KIND.choices():
  1099. rv.append(kind)
  1100. return rv
  1101. @property
  1102. def hide_consume_kinds_user(self):
  1103. rv = []
  1104. if 'hiddenUsedTime' in self.features:
  1105. rv.append(DEALER_CONSUMPTION_AGG_KIND.DURATION)
  1106. if 'hidden_coins' in self.features:
  1107. rv.append(DEALER_CONSUMPTION_AGG_KIND.COIN)
  1108. left_features = set(self.features) - {'hiddenUsedTime', 'hidden_coins'}
  1109. for feature in left_features:
  1110. if feature.startswith('hide_') and feature.endswith('_for_user'):
  1111. kind = feature.replace('hide_', '').replace('_for_user', '')
  1112. if kind in DEALER_CONSUMPTION_AGG_KIND.choices():
  1113. rv.append(kind)
  1114. return rv
  1115. @property
  1116. def withdraw_sms_provider(self):
  1117. return agentWithdrawSMSProvider
  1118. @property
  1119. def withdraw_sms_phone_number(self):
  1120. return str(self.username)
  1121. def incr_income(self, source, source_key, money):
  1122. # type: (str, str, RMB)->bool
  1123. assert isinstance(money, RMB), 'money has to be a RMB instance'
  1124. assert source in AGENT_INCOME_SOURCE.choices(), 'not support this source'
  1125. assert source_key, 'source key must not be none'
  1126. income_type = AgentConst.MAP_SOURCE_TO_TYPE[source]
  1127. query = {'_id': ObjectId(self.id)}
  1128. update = {
  1129. '$inc': {
  1130. 'aggregatedIncome.{source}'.format(source=source): money.mongo_amount,
  1131. 'incomeMap.{source}'.format(source=source): money.mongo_amount,
  1132. '{filed}.{key}.balance'.format(filed=AgentConst.MAP_TYPE_TO_FIELD[income_type],
  1133. key=source_key): money.mongo_amount
  1134. },
  1135. }
  1136. result = self.get_collection().update_one(query, update, upsert=False) # type: UpdateResult
  1137. return bool(result.modified_count == 1)
  1138. def decr_income(self, source, source_key, money): # type:(str, str, RMB) -> bool
  1139. """
  1140. 扣除代理商的收益
  1141. 必须由调用方保证不会调用重入
  1142. """
  1143. return self.incr_income(source, source_key, -money)
  1144. def new_withdraw_record(self, withdraw_gateway, pay_entity, source_key, income_type, amount, pay_type, manual,
  1145. recurrent):
  1146. # type: (WithdrawGateway, BankCard, str, str, RMB, str, bool, bool) -> WithdrawRecord
  1147. if income_type == AGENT_INCOME_TYPE.DEALER_WITHDRAW_FEE:
  1148. withdraw_fee_ratio = Permillage('0.00') # type: Permillage
  1149. else:
  1150. withdraw_fee_ratio = Const.PLATFORM_DEFAULT_WITHDRAW_FEE_RATIO # type: Permillage
  1151. service_fee = amount * withdraw_fee_ratio.as_ratio # type: RMB
  1152. withdraw_agent = withdraw_gateway.occupant # type: Agent
  1153. has_bank_fee = withdraw_agent.dealerBankWithdrawFee and \
  1154. self.bankWithdrawFee and \
  1155. pay_type == WITHDRAW_PAY_TYPE.BANK
  1156. if has_bank_fee:
  1157. bank_trans_fee = min(RMB('25.00'), max(RMB('0.10'), amount * Permillage('1').as_ratio))
  1158. else:
  1159. bank_trans_fee = RMB('0.00')
  1160. actual_pay = amount - service_fee - bank_trans_fee # type: RMB
  1161. return WithdrawRecord.create(self,
  1162. withdraw_gateway = withdraw_gateway,
  1163. pay_entity = pay_entity,
  1164. source_key = source_key,
  1165. income_type = income_type,
  1166. pay_type = pay_type,
  1167. fund_map = {
  1168. 'amount': amount,
  1169. 'serviceFee': service_fee,
  1170. 'actualPay': actual_pay,
  1171. 'bankTransFee': bank_trans_fee,
  1172. 'withdrawFeeRatio': withdraw_fee_ratio,
  1173. 'partition': []
  1174. },
  1175. manual = manual,
  1176. recurrent = recurrent)
  1177. def withdraw_bank_card(self, bank_card_no = None):
  1178. # type: (str)->BankCard
  1179. bank_card = None
  1180. if not bank_card_no:
  1181. if len(self.bankcards) > 0:
  1182. bank_card = self.bankcards[0]
  1183. else:
  1184. bank_card = BankCard.objects(cardNo = bank_card_no).first()
  1185. return bank_card
  1186. def new_withdraw_handler(self, record):
  1187. # type: (WithdrawRecord) -> WithdrawHandler
  1188. from apps.web.agent.withdraw import AgentWithdrawHandler
  1189. return AgentWithdrawHandler(self, record)
  1190. def get_bound_pay_openid(self, key):
  1191. # type: (str)->str
  1192. pay_openid_map = self.payOpenIdMap # type: dict
  1193. bound = pay_openid_map.get(key, BoundOpenInfo(openId = '')) # type: BoundOpenInfo
  1194. return str(bound.openId)
  1195. def set_bound_pay_openid(self, key, **payload):
  1196. # type: (str, Dict)->None
  1197. self.payOpenIdMap[key] = BoundOpenInfo(**payload)
  1198. def get_online_moni_app(self):
  1199. apps = MoniApp.objects.filter(appId__in = self.moniAppList, status=MoniAppStatus.ADDING).order_by('-priority')
  1200. if apps.count() == 0:
  1201. return {}
  1202. app = apps.first() # type: MoniApp
  1203. return {
  1204. 'appId': app.appId,
  1205. 'secret': app.secret,
  1206. 'title': app.title,
  1207. 'appName': app.appName,
  1208. 'desc': app.desc
  1209. }
  1210. @property
  1211. def agentIds(self):
  1212. # 自身 有agentId 配置的有公众号的,返回自身的id, 没有配置公众号的, 找到厂商的祝代理商的id 以及自身的id返回
  1213. if hasattr(self, "wechatUserManagerialApp") and self.wechatUserManagerialApp:
  1214. return [str(self.id)]
  1215. manager = Manager.objects.get(id = self.managerId)
  1216. agent = Agent.objects.get(id = str(manager.primeAgentId))
  1217. return [str(agent.id), str(self.id)]
  1218. def is_my_product_user(self, user):
  1219. return str(self.id) == user.productAgentId
  1220. @property
  1221. def my_avatar(self):
  1222. if self.avatar:
  1223. return self.avatar
  1224. logo = self.product_logo
  1225. if not logo:
  1226. logo = ''
  1227. return logo
  1228. def check_merchant_conditions(self): # type:()->None
  1229. """ 商户确认开通前的检查 """
  1230. if not self.ledgerAppJDAggre:
  1231. raise MerchantError(u"商户尚未绑定,开通失败")
  1232. def confirm_merchant(self, source):
  1233. """ 商户已经是确认状态所需要处理的事情 """
  1234. if not source.support_jdaggre():
  1235. return
  1236. if not source.owner == self:
  1237. return
  1238. # 首先获取自己的商户
  1239. app = source.create_app()
  1240. self.ledgerAppJDAggre = app
  1241. return self.save()
  1242. def success_merchant(self):
  1243. """ 确认商户开通的动作 """
  1244. return
  1245. @property
  1246. def merchantRegister(self):
  1247. return self
  1248. @property
  1249. def abnormal(self):
  1250. return self.status == self.Status.ABNORMAL
  1251. @property
  1252. def myLedgerMerchantApp(self):
  1253. """
  1254. 商户分账APP
  1255. """
  1256. if not self.ledgerAppJDAggre:
  1257. if not self.payAppJDAggre:
  1258. return None
  1259. else:
  1260. return self.payAppJDAggre.fetch()
  1261. return self.ledgerAppJDAggre.fetch()
  1262. def get_merchant_split_id(self):
  1263. """
  1264. 获取商户号绑定的email
  1265. :return:
  1266. """
  1267. app = self.myLedgerMerchantApp # type: PayAppBase
  1268. if not app:
  1269. return None
  1270. else:
  1271. return app.split_id
  1272. @property
  1273. def deviceIncomeShow(self):
  1274. """
  1275. 兼容之前数据库字段的方式
  1276. """
  1277. if "deviceIncomeShow" in self.features:
  1278. return True
  1279. return getattr(self, '_deviceIncomeShow', False)
  1280. @deviceIncomeShow.setter
  1281. def deviceIncomeShow(self, value):
  1282. """
  1283. 兼容之前的数据库 防止报错
  1284. """
  1285. setattr(self, '_deviceIncomeShow', value)
  1286. class AgentIncomeReport(Searchable):
  1287. agentId = StringField(verbose_name = '代理商ID')
  1288. detail = StrictDictField(verbose_name = '详情', default = {})
  1289. source = StringField(verbose_name = '收入来源')
  1290. amount = MonetaryField(verbose_name = '数额', default = RMB(0))
  1291. mchid = StringField(verbose_name = '资金账号')
  1292. date = StringField(verbose_name = '日期', default = lambda: datetime.datetime.now().strftime(Const.DATE_FMT))
  1293. dateTimeAdded = DateTimeField(verbose_name = '生成时间', default = datetime.datetime.now)
  1294. meta = {"collection": "agent_income_reports", "db_alias": "report"}
  1295. def __repr__(self):
  1296. return '<AgentIncomeReport agentId=%s, source=%s, date=%s>' % (self.agentId, self.source, self.date)
  1297. @property
  1298. def title(self):
  1299. if self.source == AGENT_INCOME_SOURCE.DEALER_WITHDRAW_FEE:
  1300. return u'提现收益-经销商({}) 提现金额({})'.format(str(self.detail.get('name', '')), str(self.detail.get('withdrawAmount', '')))
  1301. elif self.source == AGENT_INCOME_SOURCE.DEALER_CARD_FEE:
  1302. return u'流量卡收益-经销商(%s) 充值(%s)' % (self.detail.get('name', ''), self.detail.get('sum_of_price', ''))
  1303. elif self.source == AGENT_INCOME_SOURCE.AD:
  1304. return u'广告收益-广告(%s) 设备(%s) 经销商(%s)' \
  1305. % (self.detail.get('adId', ''), self.detail.get('logicalCode', ''), self.detail.get('dealer', ''))
  1306. elif self.source == AGENT_INCOME_SOURCE.DEALER_DEVICE_FEE:
  1307. return u'设备收益-经销商(%s) 设备(%s) 地址(%s)' \
  1308. % (self.detail.get('name', ''), self.detail.get('logicalCode', ''), self.detail.get('groupName', ''))
  1309. elif self.source == AGENT_INCOME_SOURCE.INSURANCE:
  1310. return u'保险收益-经销商({}) 地址({})'.format(self.detail.get('name', ''), self.detail.get('groupName', ''))
  1311. @classmethod
  1312. def get_income_list(cls, **filters):
  1313. if 'endDate' in filters:
  1314. filters['date__lt'] = filters.pop('endDate')
  1315. return cls.objects(**filters).order_by('-createdTime')
  1316. class MoniApp(Searchable):
  1317. # 公众号的原始属性 其中appToken用于微信验证服务器 由我们自行设置
  1318. appName = StringField(verbose_name=u"公众号的名称", default="")
  1319. appid = StringField(verbose_name=u"appId", unique=True)
  1320. rawAppId = StringField(verbose_name=u"公众号的微信号", unique=True)
  1321. secret = StringField(verbose_name=u"秘钥", default="")
  1322. appToken = StringField(verbose_name="token", default="")
  1323. appType = StringField(verbose_name="公众号类型", default="wechat")
  1324. agentId = StringField(verbose_name=u"公众号所属的Agent")
  1325. priority = IntField(verbose_name="加粉的数量", default=0)
  1326. status = IntField(verbose_name="当前的状态", default=MoniAppStatus.ADDING, choices=MoniAppStatus.choices())
  1327. desc = StringField(verbose_name='展示描述', default = '')
  1328. title = StringField(verbose_name=u'展示加粉的说明title', default="")
  1329. # TODO 需要将agentId 以及status 设置为联合索引确保唯一性
  1330. meta = {"collection": "moni_app", "db_alias": "default"}
  1331. def __str__(self):
  1332. return "<{}>-<{}>".format(self.appName, self.rawAppId)
  1333. @property
  1334. def appId(self):
  1335. return self.appid
  1336. @property
  1337. def occupantId(self):
  1338. return self.agentId
  1339. @classmethod
  1340. def get_app_by_raw(cls, rawAppId):
  1341. try:
  1342. app = cls.objects.get(rawAppId=rawAppId)
  1343. except DoesNotExist:
  1344. return None
  1345. return app
  1346. @classmethod
  1347. def get_app_by_agent(cls, agentId):
  1348. """
  1349. 获取代理商设置的 moniApp
  1350. 找到加粉符合条件的最少的一个
  1351. """
  1352. agentId = str(agentId)
  1353. return cls.objects.filter(agentId=agentId, status=MoniAppStatus.ADDING).first()
  1354. @classmethod
  1355. def get_inhouse_app(cls):
  1356. return cls.get_app_by_agent(agentId=settings.MY_PRIMARY_AGENT_ID)
  1357. def to_dict(self):
  1358. return {
  1359. 'appid': self.appid,
  1360. 'secret': self.secret,
  1361. 'title': self.title,
  1362. 'appName': self.appName,
  1363. 'desc': self.desc
  1364. }
  1365. @classmethod
  1366. def subscribe(cls, app):
  1367. cls.objects.filter(appid=str(app.appid)).update(inc__priority=1)
  1368. @classmethod
  1369. def unSubscribe(cls, app):
  1370. cls.objects.filter(appid=str(app.appid)).update(dec__priority=1)
  1371. class MoniAppPoint(Searchable):
  1372. key = StringField(verbose_name = 'key', default = '')
  1373. name = StringField(verbose_name = 'name', default = '')
  1374. desc = StringField(verbose_name = 'desc', default = '')
  1375. meta = {"collection": "moni_app_point", "db_alias": "default"}