models.py 89 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import base64
  4. import datetime
  5. import logging
  6. import os
  7. from importlib import import_module
  8. import simplejson as json
  9. from django.conf import settings
  10. from django.core.cache import cache
  11. from django.utils.module_loading import import_string
  12. from mongoengine import StringField, DateTimeField, EmbeddedDocument, DictField, BooleanField, DynamicField, ListField, \
  13. IntField, LazyReferenceField, ObjectIdField, DynamicDocument, EmbeddedDocumentField
  14. from typing import List, cast, TYPE_CHECKING
  15. from apilib.monetary import RMB
  16. from apilib.utils import flatten
  17. from apilib.utils_json import json_loads, json_dumps
  18. from apilib.utils_string import encrypt_display
  19. from apilib.utils_sys import ThreadLock
  20. from apps import serviceCache
  21. from apps.web.constant import Const, PARTITION_ROLE
  22. from apps.web.core import PayAppType, APP_KEY_DELIMITER, ROLE
  23. from apps.web.core.db import Searchable
  24. from apps.web.exceptions import UserServerException
  25. from library.alipay import AliException, AliPay, DCAliPay
  26. from library.wechatpayv3 import update_certificates
  27. logger = logging.getLogger(__name__)
  28. if TYPE_CHECKING:
  29. from apps.web.merchant.models import MerchantSourceInfo, JDOpenApplyInfo
  30. from apps.web.dealer.models import Dealer
  31. class FAQ(Searchable):
  32. """
  33. 配置FAQ,首先让经销商|终端用户看到常用问题,找不到问题再联系客服
  34. """
  35. question = StringField(verbose_name = '问题')
  36. answer = StringField(verbose_name = '答案')
  37. target = StringField(verbose_name = '目标 := (Dealer|Agent|EndUser)', default = '*')
  38. devTypeId = StringField(verbose_name = '设备类型ID,默认为普适性问题,为空', default = '')
  39. images = ListField(verbose_name = '辅助说明图像')
  40. videos = ListField(verbose_name = '辅助说明视频')
  41. upvotes = IntField(verbose_name = '该FAQ有帮助')
  42. downvotes = IntField(verbose_name = '该FAQ没有帮助')
  43. meta = {'collection': 'faqs', 'db_alias': 'default'}
  44. def to_dict(self):
  45. return {
  46. 'id': str(self.id),
  47. 'question': self.question,
  48. 'answer': self.answer,
  49. 'target': self.target,
  50. 'images': self.images,
  51. 'videos': self.videos,
  52. 'devTypeId': self.devTypeId,
  53. 'upvotes': self.upvotes,
  54. 'downvotes': self.downvotes
  55. }
  56. @classmethod
  57. def get_by_target(cls, target):
  58. return cls.objects(target__in = ['*', target])
  59. class EmbeddedApp(EmbeddedDocument):
  60. meta = {
  61. 'abstract': True
  62. }
  63. appid = StringField(verbose_name = 'appid', default = '')
  64. secret = StringField(verbose_name = "secretId", default = '', max_length = 32)
  65. name = StringField(verbose_name = "name", default = '')
  66. companyName = StringField(verbose_name = "companyName", default = '')
  67. @property
  68. def debug(self):
  69. return False
  70. @property
  71. def occupantId(self):
  72. return getattr(self, '__occupant_id__', '')
  73. @occupantId.setter
  74. def occupantId(self, occupant_id):
  75. self.__occupant_id__ = occupant_id
  76. @property
  77. def occupant(self):
  78. return getattr(self, '__occupant__', None)
  79. @occupant.setter
  80. def occupant(self, occupant):
  81. self.__occupant__ = occupant
  82. @property
  83. def __valid_check__(self):
  84. return bool(self.appid and self.secret)
  85. @property
  86. def valid(self):
  87. if hasattr(self, '__valid__'):
  88. return getattr(self, '__valid__')
  89. else:
  90. if not hasattr(self, '__valid_check__'):
  91. raise AttributeError('no __valid_check__ attribute')
  92. else:
  93. return getattr(self, '__valid_check__')
  94. @valid.setter
  95. def valid(self, value):
  96. self.__valid__ = value
  97. @property
  98. def enable(self):
  99. return getattr(self, '__enable__', True)
  100. @enable.setter
  101. def enable(self, is_enable):
  102. self.__enable__ = is_enable
  103. @classmethod
  104. def get_null_app(cls):
  105. app = cls(appid = '', secret = '')
  106. app.enable = False
  107. app.valid = True
  108. return app
  109. @staticmethod
  110. def from_json_string(app_string):
  111. app_dict = json.loads(app_string)
  112. _model = app_dict.pop('model')
  113. _cls = import_string(_model)
  114. return _cls(**app_dict)
  115. class WechatMiniApp(EmbeddedApp):
  116. mchid = StringField(verbose_name = 'mchid', null = False)
  117. apikey = StringField(verbose_name = 'apikey', null = False)
  118. sslcert_path = StringField(verbose_name = u'PERM证书路径', null = False)
  119. sslkey_path = StringField(verbose_name = u'PKCS8 PERM私钥路径', null = False)
  120. sslCert = StringField(verbose_name = u'PERM证书内容', default = '')
  121. sslKey = StringField(verbose_name = u'PKCS8 PERM私钥内容', default = '')
  122. manual_withdraw = BooleanField(verbose_name = u'是否手动提现', default = False)
  123. debug = BooleanField(verbose_name = u'是否测试账号', default = False)
  124. def __repr__(self):
  125. return '<WechatMiniApp appid=%s, mchid=%s>' % (self.appid, self.mchid)
  126. @property
  127. def __valid_check__(self):
  128. return super(WechatMiniApp, self).__valid_check__ and bool(
  129. self.mchid and self.apikey and self.ssl_cert and self.ssl_key)
  130. @property
  131. def pay_app_type(self):
  132. return PayAppType.WECHAT_MINI
  133. @property
  134. def __gateway_key__(self):
  135. _ = [
  136. self.occupantId,
  137. self.appid,
  138. self.mchid,
  139. self.occupant.role
  140. ]
  141. return APP_KEY_DELIMITER.join(_)
  142. def to_dict(self):
  143. return {
  144. 'appid': self.appid,
  145. 'secret': self.secret,
  146. 'mchid': self.mchid,
  147. 'apikey': self.apikey,
  148. 'debug': self.debug,
  149. 'name': self.name,
  150. 'companyName': self.companyName
  151. }
  152. @classmethod
  153. def get_null_app(cls):
  154. app = cls(appid = '', secret = '', mchid = '', apikey = '', sslcert_path = '', sslkey_path = '')
  155. app.enable = False
  156. app.valid = True
  157. return app
  158. @classmethod
  159. def __from_gateway_key__(cls, occupant_id, tokens):
  160. # type: (str, List)->cast(PayAppBase)
  161. appid, mchid = tokens[0], tokens[1]
  162. # TODO 这个地方确实没法解决. 必须通过AGENT来加载
  163. Agent = import_string('apps.web.agent.models.Agent')
  164. agent = Agent.objects(id = str(occupant_id)).first() # type: Agent
  165. if not agent:
  166. raise UserServerException(u'系统配置错误(第三方支付)')
  167. app = agent.wechatMiniApp # type: WechatMiniApp
  168. if not app or not app.valid:
  169. raise UserServerException(u'系统配置错误,请联系平台客服')
  170. app.occupantId = occupant_id
  171. app.occupant = agent
  172. return app
  173. def new_gateway(self, app_platform_type):
  174. from apps.web.core.payment.wechat import WechatPaymentGateway
  175. return WechatPaymentGateway(self, app_platform_type)
  176. def new_auth_bridge(self):
  177. from apps.web.core.auth.wechat import WechatAuthBridge
  178. return WechatAuthBridge(app = self)
  179. @property
  180. def ssl_cert(self):
  181. if not hasattr(self, '__ssl_cert__'):
  182. if self.sslCert.startswith('-----BEGIN'):
  183. setattr(self, '__ssl_cert__', self.sslCert)
  184. elif self.sslcert_path.startswith('-----BEGIN'):
  185. setattr(self, '__ssl_cert__', self.sslcert_path)
  186. else:
  187. with open(self.sslcert_path) as fp:
  188. setattr(self, '__ssl_cert__', fp.read())
  189. return str(getattr(self, '__ssl_cert__'))
  190. @property
  191. def ssl_key(self):
  192. if not hasattr(self, '__ssl_key__'):
  193. if self.sslKey.startswith('-----BEGIN'):
  194. setattr(self, '__ssl_key__', self.sslKey)
  195. elif self.sslkey_path.startswith('-----BEGIN'):
  196. setattr(self, '__ssl_key__', self.sslkey_path)
  197. else:
  198. with open(self.sslkey_path) as fp:
  199. setattr(self, '__ssl_key__', fp.read())
  200. return str(getattr(self, '__ssl_key__'))
  201. class WechatAuthApp(EmbeddedApp):
  202. def __repr__(self): return '<WechatAuthApp appid=%s>' % (self.appid,)
  203. def to_dict(self):
  204. return {
  205. 'appid': self.appid,
  206. 'secret': self.secret,
  207. 'name': self.name,
  208. 'companyName': self.companyName
  209. }
  210. @property
  211. def client(self):
  212. if hasattr(self, '__client__'):
  213. return getattr(self, '__client__')
  214. class WechatManagerApp(EmbeddedApp):
  215. templateIdMap = DictField(verbose_name = "微信推送消息模版ID字典", default = {})
  216. MESSAGE_TEMPLATE = {
  217. 'feedback': {
  218. 'templateId': '',
  219. 'context': json.dumps({
  220. "first": {
  221. "value": u'${title}',
  222. "color": "#173177"
  223. },
  224. "keyword1": {
  225. "value": u'${nickname}',
  226. "color": "#173177"
  227. },
  228. "keyword2": {
  229. "value": '${feedbackTime}',
  230. "color": "#173177"
  231. },
  232. "remark": {
  233. "value": "客户就是上帝,请及时处理!",
  234. "color": "#173177"
  235. }
  236. })
  237. },
  238. 'daily_income': {
  239. 'templateId': '',
  240. 'context': json.dumps({
  241. 'first': {
  242. 'value': u'${title}',
  243. 'color': '#173177'
  244. },
  245. 'keyword1': {
  246. "value": '${reportTime}',
  247. "color": '#173177'
  248. },
  249. 'keyword2': {
  250. 'value': u'${report}',
  251. 'color': '#173177'
  252. },
  253. 'remark': {
  254. 'value': u'祝您今日工作愉快!',
  255. 'color': '#173177'
  256. }
  257. })
  258. },
  259. 'new_payment_order': {
  260. 'templateId': '',
  261. 'context': json.dumps({
  262. 'first': {
  263. 'value': u'${title}',
  264. 'color': '#173177'
  265. },
  266. 'keyword1': {
  267. 'value': u'${customer}',
  268. 'color': '#173177'
  269. },
  270. 'keyword2': {
  271. 'value': u'${income}',
  272. 'color': '#173177'
  273. },
  274. 'remark': {
  275. 'value': u'祝您生意兴隆!',
  276. 'color': '#173177'
  277. }
  278. })
  279. },
  280. 'abnormal_device_offline': {
  281. 'templateId': '',
  282. 'context': json.dumps({
  283. 'first': {
  284. 'value': u'${title}',
  285. 'color': '#173177'
  286. },
  287. 'keyword1': {
  288. "value": u'${device}',
  289. "color": '#173177'
  290. },
  291. 'keyword2': {
  292. 'value': '${notifyTime}',
  293. 'color': '#173177'
  294. },
  295. 'remark': {
  296. 'value': u'感谢您的使用,小客服随时为您服务',
  297. 'color': '#173177'
  298. }
  299. })
  300. },
  301. 'device_fault': {
  302. 'templateId': '',
  303. 'context': json.dumps({
  304. 'first': {
  305. 'value': u'${title}',
  306. 'color': '#173177'
  307. },
  308. 'keyword1': {
  309. "value": u'${device}',
  310. "color": '#173177'
  311. },
  312. 'keyword2': {
  313. 'value': u'${location}',
  314. 'color': '#173177'
  315. },
  316. 'keyword3': {
  317. 'value': u'${fault}',
  318. 'color': '#173177'
  319. },
  320. 'keyword4': {
  321. 'value': '${notifyTime}',
  322. 'color': '#173177'
  323. },
  324. 'remark': {
  325. 'value': u'感谢您的使用,小客服随时为您服务',
  326. 'color': '#173177'
  327. }
  328. })
  329. },
  330. 'sim_expire_notify': {
  331. 'templateId': '',
  332. 'context': json.dumps({
  333. 'first': {
  334. 'value': u'${title}',
  335. 'color': '#173177'
  336. },
  337. 'keyword1': {
  338. "value": u'${num}',
  339. "color": '#173177'
  340. },
  341. 'keyword2': {
  342. 'value': u'${type}',
  343. 'color': '#173177'
  344. },
  345. 'keyword3': {
  346. 'value': u'${time}',
  347. 'color': '#173177'
  348. },
  349. 'remark': {
  350. 'value': u'感谢您一直以来对我们工作的支持与帮助',
  351. 'color': '#173177'
  352. }
  353. })
  354. },
  355. 'system_alarm_notify': {
  356. 'templateId': '',
  357. 'context': json.dumps({
  358. 'first': {
  359. 'value': u'${title}',
  360. 'color': '#173177'
  361. },
  362. 'keyword1': {
  363. "value": u'${account}',
  364. "color": '#173177'
  365. },
  366. 'keyword2': {
  367. 'value': u'${object}',
  368. 'color': '#173177'
  369. },
  370. 'keyword3': {
  371. 'value': u'${content}',
  372. 'color': '#173177'
  373. },
  374. 'keyword4': {
  375. 'value': u'${time}',
  376. 'color': '#173177'
  377. },
  378. 'remark': {
  379. 'value': u'客户就是上帝,请尽快处理',
  380. 'color': '#173177'
  381. }
  382. })
  383. },
  384. # 'online_notify': {
  385. # 'templateId': '',
  386. # 'context': json.dumps({
  387. # 'first': {
  388. # 'value': u'设备正常上线',
  389. # 'color': '#173177'
  390. # },
  391. # 'keyword1': {
  392. # "value": u'正常上线',
  393. # "color": '#173177'
  394. # },
  395. # 'keyword2': {
  396. # 'value': u'${device}',
  397. # 'color': '#173177'
  398. # },
  399. # 'keyword3': {
  400. # 'value': '${devNo}',
  401. # 'color': '#173177'
  402. # },
  403. # 'keyword4': {
  404. # 'value': '${devType}',
  405. # 'color': '#173177'
  406. # },
  407. # 'keyword5': {
  408. # 'value': '${address}',
  409. # 'color': '#173177'
  410. # },
  411. # 'remark': {
  412. # 'value': u'感谢您的使用,小客服随时为您服务',
  413. # 'color': '#173177'
  414. # }
  415. # })
  416. # }
  417. }
  418. def __repr__(self):
  419. return '<{class_name} appid={appid}>'.format(class_name = self.__class__.__name__, appid = self.appid)
  420. def to_dict(self):
  421. template_id_map = []
  422. for templateName, template in self.templateIdMap.iteritems():
  423. template_id_map.append({
  424. 'key': templateName,
  425. 'name': Const.TRANSLATION[templateName] if templateName in Const.TRANSLATION else templateName,
  426. 'value': template['templateId']
  427. })
  428. for templateName, message_tmpl in self.MESSAGE_TEMPLATE.iteritems():
  429. if templateName not in self.templateIdMap.keys():
  430. template_id_map.append({
  431. 'key': templateName,
  432. 'name': Const.TRANSLATION[templateName] if templateName in Const.TRANSLATION else templateName,
  433. 'value': ''
  434. })
  435. return {
  436. 'appid': self.appid,
  437. 'secret': self.secret,
  438. 'name': self.name,
  439. 'companyName': self.companyName,
  440. 'templateIdMap': template_id_map
  441. }
  442. @classmethod
  443. def create(cls, appid, secret, templateIdMap = None, name = '', companyName = ''):
  444. __template_id_map = {}
  445. if templateIdMap:
  446. for item in templateIdMap:
  447. if item['key'] not in cls.MESSAGE_TEMPLATE:
  448. continue
  449. __template_id_map[item['key']] = {
  450. 'templateId': item['value'].strip() if item['value'] else '',
  451. 'context': cls.MESSAGE_TEMPLATE[item['key']]['context']
  452. }
  453. else:
  454. __template_id_map.update(cls.MESSAGE_TEMPLATE)
  455. return cls(appid = appid, secret = secret, templateIdMap = __template_id_map, name = name,
  456. companyName = companyName)
  457. class WechatUserManagerApp(EmbeddedApp):
  458. templateIdMap = DictField(verbose_name = u"微信推送消息模版ID字典", default = {})
  459. MESSAGE_TEMPLATE = {
  460. 'feedback_process': {
  461. 'templateId': '',
  462. 'context': json.dumps({
  463. "first": {
  464. "value": u"${title}",
  465. "color": "#173177"
  466. },
  467. "event": {
  468. "value": u"${event}",
  469. "color": "#173177"
  470. },
  471. "finish_time": {
  472. "value": '${finishTime}',
  473. "color": "#173177"
  474. },
  475. "remark": {
  476. "value": u"${remark}",
  477. "color": "#173177"
  478. }
  479. })
  480. },
  481. 'service_start': {
  482. 'templateId': '',
  483. 'context': json.dumps({
  484. 'first': {
  485. 'value': u'${title}',
  486. 'color': '#173177'
  487. },
  488. 'keyword1': {
  489. 'value': u'${service}',
  490. 'color': '#173177'
  491. },
  492. 'keyword2': {
  493. "value": u'${time}',
  494. "color": '#173177'
  495. },
  496. 'remark': {
  497. 'value': u'${remark}',
  498. 'color': '#173177'
  499. }
  500. })
  501. },
  502. 'service_complete': {
  503. 'templateId': '',
  504. 'context': json.dumps({
  505. 'first': {
  506. 'value': u'${title}',
  507. 'color': '#173177'
  508. },
  509. 'keyword1': {
  510. 'value': u'${service}',
  511. 'color': '#173177'
  512. },
  513. 'keyword2': {
  514. "value": u'${finishTime}',
  515. "color": '#173177'
  516. },
  517. 'remark': {
  518. 'value': u'${remark}',
  519. 'color': '#173177'
  520. }
  521. })
  522. },
  523. 'refund_coins': {
  524. 'templateId': '',
  525. 'context': json.dumps({
  526. 'first': {
  527. 'value': u'${title}',
  528. 'color': '#173177'
  529. },
  530. 'keyword1': {
  531. 'value': u'${backCount}',
  532. 'color': '#173177'
  533. },
  534. 'keyword2': {
  535. "value": '${finishTime}',
  536. "color": '#173177'
  537. },
  538. 'remark': {
  539. 'value': u'感谢您的支持!',
  540. 'color': '#173177'
  541. }
  542. })
  543. },
  544. 'device_fault': {
  545. 'templateId': '',
  546. 'context': json.dumps({
  547. 'first': {
  548. 'value': u'${title}',
  549. 'color': '#173177'
  550. },
  551. 'performance': {
  552. "value": u'${fault}',
  553. "color": '#173177'
  554. },
  555. 'time': {
  556. 'value': u'${notifyTime}',
  557. 'color': '#173177'
  558. },
  559. 'remark': {
  560. 'value': u'感谢您的使用,小客服随时为您服务',
  561. 'color': '#173177'
  562. }
  563. })
  564. },
  565. 'less_balance': {
  566. 'templateId': '',
  567. 'context': json.dumps({
  568. 'first': {
  569. 'value': u'${title}',
  570. 'color': '#173177'
  571. },
  572. 'keyword1': {
  573. 'value': '${account}',
  574. 'color': '#173177'
  575. },
  576. 'keyword2': {
  577. "value": '${balance}',
  578. "color": '#173177'
  579. },
  580. 'remark': {
  581. 'value': u'为了不影响您的使用,建议您赶快充值!',
  582. 'color': '#173177'
  583. }
  584. })
  585. },
  586. 'consume_notify': {
  587. 'templateId': '',
  588. 'context': json.dumps({
  589. 'first': {
  590. 'value': u'${title}',
  591. 'color': '#173177'
  592. },
  593. 'keyword1': {
  594. 'value': u'${serviceType}',
  595. 'color': '#173177'
  596. },
  597. 'keyword2': {
  598. 'value': u'${money}',
  599. 'color': '#173177'
  600. },
  601. 'keyword3': {
  602. "value": '${finishTime}',
  603. "color": '#173177'
  604. },
  605. 'remark': {
  606. 'value': u'如果确认不是合法刷卡使用,可以通过解绑卡,让卡不可用。',
  607. 'color': '#173177'
  608. }
  609. })
  610. },
  611. 'service_expired': {
  612. 'templateId': '',
  613. 'context': json.dumps({
  614. 'first': {
  615. 'value': u'${title}',
  616. 'color': '#173177'
  617. },
  618. 'keyword1': {
  619. 'value': u'${department}',
  620. 'color': '#173177'
  621. },
  622. 'keyword2': {
  623. "value": '${expiredTime}',
  624. "color": '#173177'
  625. },
  626. 'remark': {
  627. 'value': u'${remark}', # u'您可以打开公众号的个人中心,选择优惠卡卷,然后选择您即将过期的卡进行续费!',
  628. 'color': '#173177'
  629. }
  630. })
  631. },
  632. 'dev_start': {
  633. 'templateId': '',
  634. 'context': json.dumps({
  635. 'first': {
  636. 'value': u'${title}',
  637. 'color': '#173177'
  638. },
  639. 'keyword1': {
  640. "value": u'${things}',
  641. "color": '#173177'
  642. },
  643. 'keyword2': {
  644. 'value': u'${time}',
  645. 'color': '#173177'
  646. },
  647. 'remark': {
  648. 'value': u'${remark}',
  649. 'color': '#173177'
  650. }
  651. })
  652. },
  653. 'insurance_sub': {
  654. 'templateId': '',
  655. 'context': json.dumps({
  656. 'first': {
  657. 'value': u'${title}',
  658. 'color': '#000000'
  659. },
  660. 'keyword1': {
  661. "value": u'${name}',
  662. "color": '#858585'
  663. },
  664. 'keyword2': {
  665. 'value': u'${category}',
  666. 'color': '#858585'
  667. },
  668. 'keyword3': {
  669. 'value': u'${orderNo}',
  670. 'color': '#858585'
  671. },
  672. 'keyword4': {
  673. 'value': u'${money}',
  674. 'color': '#858585'
  675. },
  676. 'keyword5': {
  677. 'value': u'${validTime}',
  678. 'color': '#858585'
  679. },
  680. 'remark': {
  681. 'value': u'${remark}',
  682. 'color': '#173177'
  683. }
  684. })
  685. },
  686. 'insurance_cancel': {
  687. 'templateId': '',
  688. 'context': json.dumps({
  689. 'first': {
  690. 'value': u'${title}',
  691. 'color': '#858585'
  692. },
  693. 'keyword1': {
  694. "value": u'${orderNo}',
  695. "color": '#858585'
  696. },
  697. 'keyword2': {
  698. 'value': u'${name}',
  699. 'color': '#858585'
  700. },
  701. 'keyword3': {
  702. 'value': u'${user}',
  703. 'color': '#858585'
  704. },
  705. 'keyword4': {
  706. 'value': u'${cancelTime}',
  707. 'color': '#858585'
  708. },
  709. 'keyword5': {
  710. 'value': u'${money}',
  711. 'color': '#858585'
  712. },
  713. 'remark': {
  714. 'value': u'${remark}',
  715. 'color': '#173177'
  716. }
  717. })
  718. },
  719. }
  720. def __repr__(self):
  721. return '<{class_name} appid={appid}>'.format(class_name = self.__class__.__name__, appid = self.appid)
  722. def to_dict(self):
  723. template_id_map = []
  724. for templateName, template in self.templateIdMap.iteritems():
  725. template_id_map.append({
  726. 'key': templateName,
  727. 'name': Const.TRANSLATION[templateName] if templateName in Const.TRANSLATION else templateName,
  728. 'value': template['templateId']
  729. })
  730. for templateName, message_tmpl in self.MESSAGE_TEMPLATE.iteritems():
  731. if templateName not in self.templateIdMap.keys():
  732. template_id_map.append({
  733. 'key': templateName,
  734. 'name': Const.TRANSLATION[templateName] if templateName in Const.TRANSLATION else templateName,
  735. 'value': ''
  736. })
  737. return {
  738. 'appid': self.appid,
  739. 'secret': self.secret,
  740. 'name': self.name,
  741. 'companyName': self.companyName,
  742. 'templateIdMap': template_id_map
  743. }
  744. @classmethod
  745. def create(cls, appid, secret, templateIdMap = None, name = '', companyName = ''):
  746. __template_id_map = {}
  747. if templateIdMap:
  748. for item in templateIdMap:
  749. if item['key'] not in cls.MESSAGE_TEMPLATE:
  750. continue
  751. __template_id_map[item['key']] = {
  752. 'templateId': item['value'].strip() if item['value'] else '',
  753. 'context': cls.MESSAGE_TEMPLATE[item['key']]['context']
  754. }
  755. else:
  756. __template_id_map.update(cls.MESSAGE_TEMPLATE)
  757. return cls(appid = appid, secret = secret, templateIdMap = __template_id_map, name = name,
  758. companyName = companyName)
  759. class WechatUserSubscribeManagerApp(EmbeddedApp):
  760. templateIdMap = DictField(verbose_name = "微信推送消息模版ID字典", default = {})
  761. MESSAGE_TEMPLATE = {
  762. 'service_start': {
  763. 'templateId': '',
  764. 'context': json.dumps({
  765. 'data': {
  766. 'thing2': {'value': u'${service}'}, # 服务项目
  767. 'time4': {'value': u'${time}'}, # 开始时间
  768. 'thing5': {'value': u'${remark}'}, # 备注
  769. },
  770. })
  771. },
  772. 'service_complete': {
  773. 'templateId': '',
  774. 'context': json.dumps({
  775. 'data': {
  776. 'thing5': {'value': u'${service}'}, # 服务名称
  777. 'thing6': {'value': u'${remark}'}, # 温馨提示
  778. 'time3': {'value': u'${finishTime}'}, # 完成时间
  779. },
  780. })
  781. }
  782. }
  783. def __repr__(self):
  784. return '<{class_name} appid={appid}>'.format(class_name = self.__class__.__name__, appid = self.appid)
  785. def to_dict(self):
  786. template_id_map = []
  787. for templateName, template in self.templateIdMap.iteritems():
  788. template_id_map.append({
  789. 'key': templateName,
  790. 'name': Const.TRANSLATION[templateName] if templateName in Const.TRANSLATION else templateName,
  791. 'value': template['templateId']
  792. })
  793. for templateName, message_tmpl in self.MESSAGE_TEMPLATE.iteritems():
  794. if templateName not in self.templateIdMap.keys():
  795. template_id_map.append({
  796. 'key': templateName,
  797. 'name': Const.TRANSLATION[templateName] if templateName in Const.TRANSLATION else templateName,
  798. 'value': ''
  799. })
  800. return {
  801. 'appid': self.appid,
  802. 'secret': self.secret,
  803. 'name': self.name,
  804. 'companyName': self.companyName,
  805. 'templateIdMap': template_id_map
  806. }
  807. @classmethod
  808. def create(cls, appid, secret, templateIdMap = None, name = '', companyName = ''):
  809. __template_id_map = {}
  810. if templateIdMap:
  811. for item in templateIdMap:
  812. if item['key'] not in cls.MESSAGE_TEMPLATE:
  813. continue
  814. __template_id_map[item['key']] = {
  815. 'templateId': item['value'].strip() if item['value'] else '',
  816. 'context': cls.MESSAGE_TEMPLATE[item['key']]['context']
  817. }
  818. else:
  819. __template_id_map.update(cls.MESSAGE_TEMPLATE)
  820. return cls(appid = appid, secret = secret, templateIdMap = __template_id_map, name = name,
  821. companyName = companyName)
  822. class WechatDealerSubscribeManagerApp(EmbeddedApp):
  823. templateIdMap = DictField(verbose_name = "微信推送消息模版ID字典", default = {})
  824. MESSAGE_TEMPLATE = {
  825. 'service_start': {
  826. 'templateId': '',
  827. 'context': json.dumps({
  828. 'data': {
  829. 'character_string1': {'value': u'${orderNo}'}, # 订单号
  830. 'thing2': {'value': u'${majorDeviceType}'}, # 服务项目
  831. 'time4': {'value': u'${time}'}, # 开始时间
  832. 'thing5': {'value': u'${remarks}'}, # 备注
  833. },
  834. })
  835. },
  836. }
  837. def __repr__(self):
  838. return '<{class_name} appid={appid}>'.format(class_name = self.__class__.__name__, appid = self.appid)
  839. def to_dict(self):
  840. template_id_map = []
  841. for templateName, template in self.templateIdMap.iteritems():
  842. template_id_map.append({
  843. 'key': templateName,
  844. 'name': Const.TRANSLATION[templateName] if templateName in Const.TRANSLATION else templateName,
  845. 'value': template['templateId']
  846. })
  847. for templateName, message_tmpl in self.MESSAGE_TEMPLATE.iteritems():
  848. if templateName not in self.templateIdMap.keys():
  849. template_id_map.append({
  850. 'key': templateName,
  851. 'name': Const.TRANSLATION[templateName] if templateName in Const.TRANSLATION else templateName,
  852. 'value': ''
  853. })
  854. return {
  855. 'appid': self.appid,
  856. 'secret': self.secret,
  857. 'name': self.name,
  858. 'companyName': self.companyName,
  859. 'templateIdMap': template_id_map
  860. }
  861. @classmethod
  862. def create(cls, appid, secret, templateIdMap = None, name = '', companyName = ''):
  863. __template_id_map = {}
  864. if templateIdMap:
  865. for item in templateIdMap:
  866. if item['key'] not in cls.MESSAGE_TEMPLATE:
  867. continue
  868. __template_id_map[item['key']] = {
  869. 'templateId': item['value'].strip() if item['value'] else '',
  870. 'context': cls.MESSAGE_TEMPLATE[item['key']]['context']
  871. }
  872. else:
  873. __template_id_map.update(cls.MESSAGE_TEMPLATE)
  874. return cls(appid = appid, secret = secret, templateIdMap = __template_id_map, name = name,
  875. companyName = companyName)
  876. def get_sub_template_id_list(self):
  877. if not self.templateIdMap:
  878. return []
  879. else:
  880. try:
  881. return map(lambda _: _['templateId'], self.templateIdMap.values())
  882. except Exception as e:
  883. logger.info('error e=<{}>'.format(e))
  884. return []
  885. class JDAuthApp(EmbeddedApp):
  886. def __repr__(self): return '<JDAuthApp appid=%s>' % (self.appid,)
  887. def to_dict(self):
  888. return {
  889. 'appid': self.appid,
  890. 'secret': self.secret,
  891. 'name': self.name,
  892. 'companyName': self.companyName
  893. }
  894. class MerchantAgent(Searchable):
  895. """
  896. 将商户号 和 代理商账号绑定起来 这样方便 代理商收取手续费的时候 直接从相应的网关获取
  897. """
  898. agentNo = StringField(verbose_name=u"商户号的服务商商户号")
  899. accessKey = StringField(verbose_name=u"accessKey", default=None)
  900. secretKey = StringField(verbose_name=u"secretKey", default=None)
  901. dateTimeAdded = DateTimeField(verbose_name=u"添加进来的时间")
  902. remarks = StringField(verbose_name=u"备注", default=u"")
  903. agentSystemId = StringField(verbose_name=u"服务商的systemId(京东聚合老系统需要)", default=None)
  904. def __str__(self):
  905. return self.agentNo
  906. @classmethod
  907. def get_agent_id(cls, agentSystemId):
  908. merchantAgent = cls.objects.filter(agentSystemId=agentSystemId).first() or cls()
  909. return merchantAgent.agentId
  910. @classmethod
  911. def default_merchant_agent(cls):
  912. wflSystemId = os.environ.get("JD_SYSTEM_ID") or "112122276PSP"
  913. return cls.objects.filter(agentSystemId=wflSystemId).first()
  914. class PayAppBase(Searchable):
  915. meta = {
  916. 'abstract': True
  917. }
  918. class Function(object):
  919. PAY = 'pay'
  920. WITHDRAW = 'withdraw'
  921. companyName = StringField(verbose_name = u'公司名称', required = True, default = '')
  922. appName = StringField(verbose_name = u'应用名称', required = True, default = u'自助服务')
  923. remarks = StringField(verbose_name = u'备注', default = '')
  924. dateTimeAdded = DateTimeField(default = datetime.datetime.now, verbose_name = '添加时间')
  925. dateTimeUpdated = DateTimeField(verbose_name = '修改时间')
  926. inhouse = BooleanField(verbose_name = u'是否平台商户', default = False)
  927. debug = BooleanField(verbose_name = u'是否测试账号', default = False)
  928. functions = ListField(verbose_name = u'支持功能列表', default = [Function.PAY])
  929. @property
  930. def pay_app_type(self):
  931. raise AttributeError('no pay_app_type attribute')
  932. @property
  933. def occupantId(self):
  934. return getattr(self, '__occupant_id__', '')
  935. @occupantId.setter
  936. def occupantId(self, occupant_id):
  937. self.__occupant_id__ = occupant_id
  938. @property
  939. def occupant(self):
  940. _obj = getattr(self, '__occupant__', None)
  941. if not _obj:
  942. _obj = ROLE.from_role_id(self.role, self.occupantId)
  943. self.__occupant__ = _obj
  944. return _obj
  945. @occupant.setter
  946. def occupant(self, occupant):
  947. self.__occupant__ = occupant
  948. self.role = occupant.role
  949. @property
  950. def role(self):
  951. return self.__role__
  952. @role.setter
  953. def role(self, role):
  954. self.__role__ = role
  955. @property
  956. def valid(self):
  957. if hasattr(self, '__valid__'):
  958. return getattr(self, '__valid__')
  959. else:
  960. if not hasattr(self, '__valid_check__'):
  961. raise AttributeError('no __valid_check__ attribute')
  962. else:
  963. return getattr(self, '__valid_check__')
  964. @valid.setter
  965. def valid(self, value):
  966. self.__valid__ = value
  967. @property
  968. def enable(self):
  969. # type: ()->bool
  970. return getattr(self, '__enable__', True)
  971. @enable.setter
  972. def enable(self, value):
  973. # type: (bool)->None
  974. self.__enable__ = value
  975. @property
  976. def __gateway_key__(self):
  977. raise AttributeError('no __gateway_key__ attribute')
  978. @property
  979. def __source_key__(self):
  980. raise AttributeError('no __source_key__ attribute')
  981. @classmethod
  982. def get_collection(cls):
  983. return cls._get_collection()
  984. def to_dict(self):
  985. raise NotImplementedError('must implement to_dict method')
  986. @classmethod
  987. def list(cls, page_index, page_size, search_key):
  988. cursor = cls.objects().search(search_key).order_by('-dateTimeAdded')
  989. total = cursor.count()
  990. items = cursor.paginate(page_index, page_size)
  991. return total, [_.to_dict(True) for _ in items]
  992. @classmethod
  993. def __from_gateway_key__(cls, occupant_id, tokens):
  994. # type: (str, List)->cast(PayAppBase)
  995. raise NotImplementedError('must implement from_gateway_key class method')
  996. def new_gateway(self, app_platform_type):
  997. raise NotImplementedError('must implement new_gateway')
  998. def new_withdraw_gateway(self, is_ledger = True, gateway_version = None):
  999. raise NotImplementedError('must implement new_withdraw_gateway')
  1000. @property
  1001. def split_id(self):
  1002. raise NotImplementedError('must implement split_id')
  1003. def refund_bill_split_list(self, partition_map):
  1004. raise NotImplementedError('must implement refund_bill_split_list')
  1005. def bill_split_rule(self, partition_map):
  1006. raise NotImplementedError('must implement refund_bill_split_list')
  1007. class JosShopInfo(EmbeddedDocument):
  1008. """
  1009. 京东云支付的相关信息
  1010. 其中门店ID很关键
  1011. """
  1012. # 品牌ID 查看方式 品牌管理 -> 品牌认证 -> 品牌ID
  1013. brandId = StringField(verbose_name = u"京东引流 品牌ID", default = "13499")
  1014. # 企业BID 查看方式 品牌管理 -> 企业认证 -> 企业BID
  1015. bizId = StringField(verbose_name = u"京东引流 企业BID", default = "100000000006317")
  1016. # 外部门店ID 重要
  1017. # 这个门店的编号直接影响用户的欠款去向 千万注意
  1018. # jos后台的逻辑是 通过 exStoreId 查找到
  1019. # 绑定的京东商户, 通过京东商户找到绑定的钱包 最后用户付款的钱直接到对应钱包里面
  1020. exStoreId = StringField(verbose_name = u"京东引流的外部门店ID,需要web端操作绑定", required = True)
  1021. # 和门店相关联的商品的ID 商品发布后一定需要关联门店,可以多个门店关联一个商品 也可以一个商品关联多个门店
  1022. exSkuId = StringField(verbose_name = u"京东引流的外部商品ID,需要web端操作绑定", default = "100001")
  1023. brandName = StringField(verbose_name = u"品牌名称, 需要和品牌ID对应", default = u"微付乐")
  1024. tradeName = StringField(verbose_name = u"商品名称,需要和商品ID对应", default = u"测试商品")
  1025. class WechatPayApp(PayAppBase):
  1026. """
  1027. 对于V1接口, 需要sslKey(证书私钥)和sslCert(证书字符串)来调用接口, 使用apikey来加解密信息
  1028. 对于V3接口, 需要sslKey(证书私钥)和app_serial_number(证书序列号)来调用接口, 使用apikey_v3来加解密信息
  1029. """
  1030. appid = StringField(verbose_name = u'应用ID', required = True, null = False)
  1031. secret = StringField(verbose_name = u"secretId", required = True, null = False, max_length = 32)
  1032. mchid = StringField(verbose_name = u'mchid', null = False)
  1033. sslKey = StringField(verbose_name = u'PKCS8 PERM私钥内容', default = '')
  1034. apikey = StringField(verbose_name = u'apikey', null = False)
  1035. sslCert = StringField(verbose_name = u'PERM证书内容', default = '')
  1036. apikey_v3 = StringField(verbose_name = u'v3 api key. 加解密V3接口敏感信息', default = None)
  1037. app_serial_number = StringField(verbose_name = u'商户证书序列号', default = None)
  1038. platform_certificates = ListField(verbose_name = u'平台证书列表', default = None)
  1039. # 暂时保留
  1040. sslcert_path = StringField(verbose_name = '证书pem格式', default = None)
  1041. sslkey_path = StringField(verbose_name = '证书密钥pem格式', default = None)
  1042. rawAppId = StringField(verbose_name = u'原始ID', default = '')
  1043. rootca_path = StringField(verbose_name = u'root证书路径', default = '')
  1044. rootCA = StringField(verbose_name = u'root证书', default = '')
  1045. withdrawV3 = BooleanField(verbose_name = u'是否使用V3接口对微信转账', default = False)
  1046. manual_withdraw = BooleanField(verbose_name = u'是否手动提现', default = False)
  1047. functions = ListField(verbose_name = u'支持功能列表', default = [PayAppBase.Function.PAY, PayAppBase.Function.WITHDRAW])
  1048. search_fields = ('appid', 'mchid', 'name', 'remarks')
  1049. meta = {
  1050. 'indexes': [
  1051. {
  1052. 'fields': ['appid', 'mchid'], 'unique': True
  1053. },
  1054. ],
  1055. 'collection': 'wechat_pay_app',
  1056. 'db_alias': 'default'
  1057. }
  1058. def __repr__(self):
  1059. return '<WechatPayApp appid={}>'.format(self.appid)
  1060. @property
  1061. def __valid_check__(self):
  1062. return bool(self.appid and self.secret and self.mchid and self.ssl_key and self.ssl_cert and self.apikey)
  1063. @property
  1064. def pay_app_type(self):
  1065. return PayAppType.WECHAT
  1066. @property
  1067. def __gateway_key__(self):
  1068. _ = [
  1069. self.occupantId,
  1070. self.appid,
  1071. self.mchid,
  1072. self.occupant.role
  1073. ]
  1074. return APP_KEY_DELIMITER.join(_)
  1075. @property
  1076. def __source_key__(self):
  1077. return APP_KEY_DELIMITER.join([
  1078. self.occupantId,
  1079. self.mchid
  1080. ])
  1081. def to_dict(self, shadow = False):
  1082. return {
  1083. 'id': str(self.id),
  1084. 'appid': self.appid,
  1085. 'mchid': self.mchid,
  1086. 'secret': encrypt_display(self.secret) if shadow else self.secret,
  1087. 'apikey': encrypt_display(self.apikey) if shadow else self.apikey,
  1088. 'ssl_key': base64.b64encode(self.ssl_key),
  1089. 'ssl_cert': base64.b64encode(self.ssl_cert),
  1090. 'manual_withdraw': self.manual_withdraw,
  1091. 'companyName': self.companyName,
  1092. 'appName': self.appName,
  1093. 'remarks': self.remarks
  1094. }
  1095. @classmethod
  1096. def get_null_app(cls):
  1097. app = cls(appid = '',
  1098. secret = '',
  1099. mchid = '',
  1100. apikey = '',
  1101. sslKey = '',
  1102. sslCert = '',
  1103. sslcert_path = '',
  1104. sslkey_path = '',
  1105. manual_withdraw = False)
  1106. app.enable = False
  1107. app.valid = True
  1108. return app
  1109. @classmethod
  1110. def __from_gateway_key__(cls, occupant_id, tokens):
  1111. # type: (str, List)->cast(PayAppBase)
  1112. appid, mchid = tokens[0], tokens[1]
  1113. app = cls.objects(appid = appid, mchid = mchid).first() # type: WechatPayApp
  1114. if not app:
  1115. raise UserServerException(u'系统配置错误,请联系平台客服')
  1116. app.occupantId = occupant_id
  1117. try:
  1118. app.role = tokens[2]
  1119. except:
  1120. app.role = ROLE.agent
  1121. return app
  1122. @classmethod
  1123. def __from_source_key__(cls, occupant_id, tokens):
  1124. # type: (str, List)->cast(PayAppBase)
  1125. # TODO 查找最新加的商户号. 需要增加商户号管理功能,可以禁止,设置active等功能
  1126. app = cls.objects(mchid = tokens[0]).order_by('-dateTimeAdded').first()
  1127. if not app:
  1128. raise UserServerException(u'系统配置错误(第三方支付),请联系平台客服')
  1129. app.occupantId = occupant_id
  1130. return app
  1131. def new_gateway(self, app_platform_type):
  1132. from apps.web.core.payment.wechat import WechatPaymentGateway
  1133. return WechatPaymentGateway(self)
  1134. def new_withdraw_gateway(self, is_ledger = True, gateway_version = 'v1'):
  1135. from apps.web.core.payment.wechat import WechatWithdrawGateway
  1136. if not self.enable:
  1137. is_ledger = False
  1138. if gateway_version == 'v3':
  1139. if not self.withdrawV3 or not self.apikey_v3:
  1140. return None
  1141. return WechatWithdrawGateway(self, gateway_version = gateway_version, is_ledger = is_ledger)
  1142. @property
  1143. def ssl_cert(self):
  1144. if not hasattr(self, '__ssl_cert__'):
  1145. try:
  1146. if self.sslCert.startswith('-----BEGIN'):
  1147. setattr(self, '__ssl_cert__', self.sslCert)
  1148. elif self.sslcert_path.startswith('-----BEGIN'):
  1149. setattr(self, '__ssl_cert__', self.sslcert_path)
  1150. else:
  1151. with open(self.sslcert_path) as fp:
  1152. setattr(self, '__ssl_cert__', fp.read())
  1153. except Exception as e:
  1154. logger.error('{} ssl_cert exception = {}'.format(repr(self), e.message))
  1155. setattr(self, '__ssl_cert__', '')
  1156. return getattr(self, '__ssl_cert__')
  1157. @property
  1158. def ssl_key(self):
  1159. if not hasattr(self, '__ssl_key__'):
  1160. try:
  1161. if self.sslKey.startswith('-----BEGIN'):
  1162. setattr(self, '__ssl_key__', self.sslKey)
  1163. elif self.sslkey_path.startswith('-----BEGIN'):
  1164. setattr(self, '__ssl_key__', self.sslkey_path)
  1165. else:
  1166. with open(self.sslkey_path) as fp:
  1167. setattr(self, '__ssl_key__', fp.read())
  1168. except Exception as e:
  1169. logger.error('{} ssl_key exception = {}'.format(repr(self), e.message))
  1170. setattr(self, '__ssl_key__', '')
  1171. return getattr(self, '__ssl_key__')
  1172. @update_certificates.connect
  1173. def update_v3_certificates(sender, mchid, cert_str_list):
  1174. WechatPayApp.objects(mchid = mchid).update(platform_certificates = cert_str_list)
  1175. class AliApp(PayAppBase):
  1176. appid = StringField(verbose_name = u'应用ID', required = True, null = False)
  1177. # 参数加密秘钥. 一般不加密
  1178. aesEncryptKey = StringField(verbose_name = u'加密秘钥', default = '')
  1179. # 公钥模式
  1180. app_private_key_path = StringField(verbose_name = u'支付宝应用私钥路径', default = '')
  1181. appPrivateKey = StringField(verbose_name = u'支付宝应用私钥. 只有测试情况下才能使用字符串', default = '')
  1182. public_key_path = StringField(verbose_name = u'支付宝账户公钥路径', default = '')
  1183. alipayPublicKey = StringField(verbose_name = u'文本形式支付宝账户公钥', default = '')
  1184. # 证书模式
  1185. app_publickey_cert_path = StringField(verbose_name = u'支付宝应用公共证书路径', default = '')
  1186. appPublicKeyCert = StringField(verbose_name = u'支付宝应用公共证书', default = '')
  1187. publickey_cert_path = StringField(verbose_name = u'支付宝公共证书路径', default = '')
  1188. publicKeyCert = StringField(verbose_name = u'支付宝公共证书', default = '')
  1189. root_cert_path = StringField(verbose_name = u'支付宝根证书路径', default = '')
  1190. rootCert = StringField(verbose_name = u'支付宝根证书', default = '')
  1191. signKeyType = StringField(verbose_name = u'签名方式(normal,cert)', default = 'normal')
  1192. # 中间字段
  1193. alipayPublicKeyContent = StringField(verbose_name = u'支付宝公钥文本', default = '')
  1194. appPublicKeyContent = StringField(verbose_name = u'应用公钥文本', default = '')
  1195. search_fields = ('appid', 'name', 'remarks')
  1196. meta = {
  1197. 'indexes': [
  1198. {
  1199. 'fields': ['appid'], 'unique': True
  1200. },
  1201. ],
  1202. 'collection': 'ali_pay_app',
  1203. 'db_alias': 'default'
  1204. }
  1205. def __repr__(self):
  1206. return '<AliApp appid={}>'.format(self.appid)
  1207. @property
  1208. def __valid_check__(self):
  1209. if not self.appid:
  1210. return False
  1211. if self.signKeyType == 'normal':
  1212. return bool(self.public_key_string and self.app_private_key_string)
  1213. if self.signKeyType == 'cert':
  1214. return bool(self.app_private_key_string and
  1215. self.public_key_cert_string and
  1216. self.root_cert_string and
  1217. self.app_public_key_cert_string)
  1218. return False
  1219. @property
  1220. def aes_encrypt_key(self):
  1221. return self.aesEncryptKey
  1222. @property
  1223. def app_private_key_string(self):
  1224. if not hasattr(self, '__app_private_key_string__'):
  1225. try:
  1226. if self.appPrivateKey.startswith('-----BEGIN'):
  1227. setattr(self, '__app_private_key_string__', self.appPrivateKey)
  1228. elif self.app_private_key_path and self.app_private_key_path.startswith('-----BEGIN'):
  1229. setattr(self, '__app_private_key_string__', self.app_private_key_path)
  1230. else:
  1231. with open(self.app_private_key_path) as fp:
  1232. setattr(self, '__app_private_key_string__', fp.read())
  1233. except Exception as e:
  1234. logger.error('{} app_private_key_string exception = {}'.format(repr(self), e.message))
  1235. setattr(self, '__app_private_key_string__', '')
  1236. return getattr(self, '__app_private_key_string__')
  1237. @property
  1238. def public_key_string(self):
  1239. if not hasattr(self, '__public_key_string__'):
  1240. try:
  1241. if self.alipayPublicKey.startswith('-----BEGIN'):
  1242. setattr(self, '__public_key_string__', self.alipayPublicKey)
  1243. elif self.public_key_path.startswith('-----BEGIN'):
  1244. setattr(self, '__public_key_string__', self.public_key_path)
  1245. else:
  1246. with open(self.public_key_path) as fp:
  1247. setattr(self, '__public_key_string__', fp.read())
  1248. except Exception as e:
  1249. logger.error('{} public_key_string exception = {}'.format(repr(self), e.message))
  1250. setattr(self, '__public_key_string__', '')
  1251. return getattr(self, '__public_key_string__')
  1252. @property
  1253. def app_public_key_cert_string(self):
  1254. if not hasattr(self, '__app_public_key_cert_string__'):
  1255. try:
  1256. if self.appPublicKeyCert.startswith('-----BEGIN'):
  1257. setattr(self, '__app_public_key_cert_string__', self.appPublicKeyCert)
  1258. else:
  1259. with open(self.appPublicKeyCert) as fp:
  1260. setattr(self, '__app_public_key_cert_string__', fp.read())
  1261. except Exception as e:
  1262. logger.error('{} app_public_key_cert_string exception = {}'.format(repr(self), e.message))
  1263. setattr(self, '__app_public_key_cert_string__', '')
  1264. return getattr(self, '__app_public_key_cert_string__')
  1265. @property
  1266. def public_key_cert_string(self):
  1267. if not hasattr(self, '__public_key_cert_string__'):
  1268. try:
  1269. if self.publicKeyCert.startswith('-----BEGIN'):
  1270. setattr(self, '__public_key_cert_string__', self.publicKeyCert)
  1271. else:
  1272. with open(self.publicKeyCert) as fp:
  1273. setattr(self, '__public_key_cert_string__', fp.read())
  1274. except Exception as e:
  1275. logger.error('{} public_key_cert_string exception = {}'.format(repr(self), e.message))
  1276. setattr(self, '__public_key_cert_string__', '')
  1277. return getattr(self, '__public_key_cert_string__')
  1278. @property
  1279. def root_cert_string(self):
  1280. if not hasattr(self, '__root_cert_string__'):
  1281. try:
  1282. if self.rootCert.startswith('-----BEGIN'):
  1283. setattr(self, '__root_cert_string__', self.rootCert)
  1284. else:
  1285. with open(self.rootCert) as fp:
  1286. setattr(self, '__root_cert_string__', fp.read())
  1287. except Exception as e:
  1288. logger.error('{} root_cert_string exception = {}'.format(repr(self), e.message))
  1289. setattr(self, '__root_cert_string__', '')
  1290. return getattr(self, '__root_cert_string__')
  1291. @property
  1292. def pay_app_type(self):
  1293. return PayAppType.ALIPAY
  1294. @property
  1295. def __gateway_key__(self):
  1296. _ = [
  1297. self.occupantId,
  1298. self.appid,
  1299. self.occupant.role
  1300. ]
  1301. return APP_KEY_DELIMITER.join(_)
  1302. def to_dict(self, shadow = False):
  1303. return {
  1304. 'id': str(self.id),
  1305. 'appid': self.appid,
  1306. 'hasAlipayAppKeyPair': bool(self.app_private_key_string is not ''),
  1307. 'public_key_path': self.public_key_path,
  1308. 'app_private_key_path': self.app_private_key_path,
  1309. 'alipayPublicKey': encrypt_display(self.alipayPublicKey) if shadow else self.alipayPublicKey,
  1310. 'appPrivateKey': encrypt_display(self.appPrivateKey) if shadow else self.appPrivateKey,
  1311. 'alipayPublicKeyContent': encrypt_display(
  1312. self.alipayPublicKeyContent) if shadow else self.alipayPublicKeyContent,
  1313. 'appPublicKeyContent': encrypt_display(self.appPublicKeyContent) if shadow else self.appPublicKeyContent,
  1314. 'debug': self.debug,
  1315. 'appName': self.appName,
  1316. 'companyName': self.companyName
  1317. }
  1318. @classmethod
  1319. def get_null_app(cls):
  1320. app = cls(appid = '', public_key_path = '', app_private_key_path = '', shadow = False)
  1321. app.enable = False
  1322. app.valid = True
  1323. return app
  1324. @classmethod
  1325. def __from_gateway_key__(cls, occupant_id, tokens):
  1326. # type: (str, List)->cast(PayAppBase)
  1327. appid = tokens[0]
  1328. app = cls.objects(appid = appid).first() # type: AliApp
  1329. if not app:
  1330. raise UserServerException(u'系统配置错误,请联系平台客服(10005)')
  1331. app.occupantId = occupant_id
  1332. try:
  1333. app.role = tokens[1]
  1334. except:
  1335. app.role = ROLE.agent
  1336. return app
  1337. def new_gateway(self, app_platform_type):
  1338. from apps.web.core.payment.ali import AliPayGateway
  1339. return AliPayGateway(self)
  1340. def new_withdraw_gateway(self, is_ledger = True, gateway_version = None):
  1341. from apps.web.core.payment.ali import AliPayWithdrawGateway
  1342. if not self.enable:
  1343. return AliPayWithdrawGateway(self, False)
  1344. else:
  1345. return AliPayWithdrawGateway(self, is_ledger)
  1346. @property
  1347. def client(self):
  1348. if hasattr(self, '__client__'):
  1349. return getattr(self, '__client__')
  1350. if self.signKeyType not in ['normal', 'cert']:
  1351. raise AliException(
  1352. errCode = -1,
  1353. errMsg = u'参数配置错误',
  1354. client = None)
  1355. params = {
  1356. 'appid': self.appid,
  1357. 'sign_type': "RSA2",
  1358. 'debug': self.debug,
  1359. 'timeout': 15
  1360. }
  1361. params.update({'app_private_key_string': self.app_private_key_string})
  1362. if self.signKeyType == 'normal':
  1363. params.update({'public_key_string': self.public_key_string})
  1364. setattr(self, '__client__', AliPay(**params))
  1365. if self.signKeyType == 'cert':
  1366. params.update({
  1367. 'public_key_cert_string': self.public_key_cert_string,
  1368. 'root_cert_string': self.root_cert_string,
  1369. 'app_public_key_cert_string': self.app_public_key_cert_string,
  1370. 'aes_encrypt_key': self.aes_encrypt_key
  1371. })
  1372. setattr(self, '__client__', DCAliPay(**params))
  1373. return getattr(self, '__client__')
  1374. class PlatformAppBase(PayAppBase):
  1375. """
  1376. 平台记账APP
  1377. """
  1378. meta = {
  1379. 'abstract': True
  1380. }
  1381. APP_NAME = ''
  1382. @property
  1383. def __valid_check__(self):
  1384. return True
  1385. @property
  1386. def __gateway_key__(self):
  1387. _ = [
  1388. settings.MY_PRIMARY_AGENT_ID,
  1389. ROLE.agent
  1390. ]
  1391. return APP_KEY_DELIMITER.join(_)
  1392. @classmethod
  1393. def get_app(cls):
  1394. app = cls()
  1395. app.role = ROLE.agent
  1396. app.occupantId = settings.MY_PRIMARY_AGENT_ID
  1397. app.enable = True
  1398. app.valid = True
  1399. app.inhouse = True
  1400. app.debug = False
  1401. app.companyName = u'武汉大源科技有限公司'
  1402. app.appName = cls.APP_NAME
  1403. return app
  1404. @classmethod
  1405. def __from_gateway_key__(cls, occupant_id, tokens):
  1406. # type: (str, List)->cast(PayAppBase)
  1407. assert occupant_id == settings.MY_PRIMARY_AGENT_ID, u'非法GatewayKey'
  1408. return cls.get_app()
  1409. class PlatformPromotionApp(PlatformAppBase):
  1410. """
  1411. 用于平台记账后 分账给其他人, 例如红包受益 广告收益等 分给经销商
  1412. """
  1413. meta = {
  1414. 'abstract': True
  1415. }
  1416. APP_NAME = u'平台补贴'
  1417. def __repr__(self):
  1418. return '<platform promotion App>'
  1419. def new_gateway(self, app_platform_type):
  1420. from apps.web.core.payment.platform import PlatformPromotionPaymentGateway
  1421. return PlatformPromotionPaymentGateway(self, app_platform_type)
  1422. @property
  1423. def pay_app_type(self):
  1424. return PayAppType.PLATFORM_PROMOTION
  1425. class PlatformReconcileApp(PlatformAppBase):
  1426. """
  1427. 平台对账后建立订单分账
  1428. """
  1429. meta = {
  1430. 'abstract': True
  1431. }
  1432. APP_NAME = u'平台对账'
  1433. def __repr__(self):
  1434. return '<platform reconcile App>'
  1435. def new_gateway(self, app_platform_type):
  1436. from apps.web.core.payment.platform import PlatformReconcilePaymentGateway
  1437. return PlatformReconcilePaymentGateway(self, app_platform_type)
  1438. @property
  1439. def pay_app_type(self):
  1440. return PayAppType.PLATFORM_RECONCILE
  1441. class PlatformWalletApp(PlatformPromotionApp):
  1442. """
  1443. 用于使用经销商余额进行SIM卡支付
  1444. """
  1445. meta = {
  1446. 'abstract': True
  1447. }
  1448. def __repr__(self):
  1449. return '<platform wallet App>'
  1450. def new_gateway(self, app_platform_type):
  1451. from apps.web.core.payment.platform import PlatformWalletPaymentGateway
  1452. return PlatformWalletPaymentGateway(self, app_platform_type)
  1453. @property
  1454. def pay_app_type(self):
  1455. return PayAppType.PLATFORM_WALLET
  1456. class LedgerConsumeApp(PayAppBase):
  1457. meta = {
  1458. 'abstract': True
  1459. }
  1460. APP_NAME = u'消费分账'
  1461. @property
  1462. def pay_app_type(self):
  1463. return PayAppType.LEDGER_CONSUME
  1464. def new_gateway(self, app_platform_type):
  1465. from apps.web.core.payment.consume import LedgerConsumePaymentGateway
  1466. return LedgerConsumePaymentGateway(self, app_platform_type)
  1467. @classmethod
  1468. def get_app(cls, dealer): # type:(Dealer) -> LedgerConsumeApp
  1469. agent = dealer.productAgent
  1470. app = cls()
  1471. app.role = ROLE.agent
  1472. app.occupantId = str(agent.id)
  1473. app.enable = True
  1474. app.valid = True
  1475. app.inhouse = True
  1476. app.debug = False
  1477. app.appName = cls.APP_NAME
  1478. return app
  1479. class JDOpenPayApp(PayAppBase):
  1480. customerNum = StringField(verbose_name = u'商户编号', unique = True, required = True, null = False)
  1481. shopNum = StringField(verbose_name = u'店铺编号', required = True, null = False)
  1482. accessKey = StringField(verbose_name=u"accessKey", default=None)
  1483. secretKey = StringField(verbose_name=u"secretKey", default=None)
  1484. agent = LazyReferenceField(document_type=MerchantAgent)
  1485. meta = {
  1486. 'indexes': [
  1487. {
  1488. 'fields': ['customerNum'], 'unique': True
  1489. },
  1490. {
  1491. 'fields': ['wxSubMchId']
  1492. }
  1493. ],
  1494. 'collection': 'jdopen_pay_app',
  1495. 'db_alias': 'default'
  1496. }
  1497. def __repr__(self):
  1498. return '<JDOpenPayApp customerNum={} shopNum={}>'.format(self.customerNum, self.shopNum)
  1499. @property
  1500. def __valid_check__(self):
  1501. # return bool(self.customerNum) and bool(self.shopNum)
  1502. return bool(self.customerNum)
  1503. @property
  1504. def pay_app_type(self):
  1505. return PayAppType.JD_OPEN
  1506. @property
  1507. def __gateway_key__(self):
  1508. _ = [
  1509. self.occupantId,
  1510. self.customerNum,
  1511. self.occupant.role
  1512. ]
  1513. return APP_KEY_DELIMITER.join(_)
  1514. @classmethod
  1515. def get_null_app(cls):
  1516. app = cls(merchant_no = '')
  1517. app.enable = False
  1518. app.valid = True
  1519. return app
  1520. @classmethod
  1521. def __from_gateway_key__(cls, occupant_id, tokens):
  1522. # type: (str, List)->cast(PayAppBase)
  1523. customerNum = tokens[0]
  1524. app = cls.objects(customerNum = customerNum).first() # type: JDOpenPayApp
  1525. if not app:
  1526. raise UserServerException(u'系统配置错误,请联系平台客服')
  1527. app.occupantId = occupant_id
  1528. try:
  1529. app.role = tokens[1]
  1530. except:
  1531. app.role = ROLE.agent
  1532. return app
  1533. def new_gateway(self, app_platform_type):
  1534. from apps.web.core.payment.jdopen import JDOpenPaymentGateway
  1535. return JDOpenPaymentGateway(self, app_platform_type)
  1536. def to_dict(self, shadow = True):
  1537. return {
  1538. 'id': str(self.id),
  1539. 'customerNum': self.customerNum,
  1540. 'companyName': self.companyName,
  1541. 'appName': self.appName,
  1542. 'remarks': self.remarks
  1543. }
  1544. @classmethod
  1545. def get_or_create_by_merchant(cls, source): # type: (JDOpenApplyInfo) -> JDOpenPayApp
  1546. app = cls.objects.filter(agentNum=source.agentNum, customerNum=source.customerNum)
  1547. if not app:
  1548. app = cls(agentNum=source.agentNum, customerNum=source.customerNum)
  1549. app.settleNum = source.settleNum
  1550. app.shopNum = source.shopNum
  1551. app.accessKey, app.secretKey = source.get_access_and_secret()
  1552. return app.save()
  1553. @property
  1554. def split_id(self):
  1555. return self.customerNum
  1556. def bill_split_rule(self, partition_map):
  1557. """
  1558. 分账list必须全部包括收款方和分账方,分账金额保留两位小数,且分账金额相加需等于订单金额
  1559. :return:
  1560. """
  1561. partitions = list(flatten(partition_map.values()))
  1562. rv = []
  1563. for partition in partitions:
  1564. if RMB(partition['money']) > RMB(0):
  1565. rv.append({
  1566. 'customerNum': partition['merchantId'],
  1567. 'amount': str(RMB(partition['money']))
  1568. })
  1569. if len(rv) <= 1:
  1570. return None, None, None
  1571. else:
  1572. return 'FIXED', 'RECEIVER', rv
  1573. def refund_bill_split_list(self, partition_map):
  1574. """
  1575. 京东退款分账规则:
  1576. 1、即使退款为0, 也必须带上拉起支付的商户号
  1577. 2、所以参与支付分账的商户, 即使金额为0, 也必须带上
  1578. :return:
  1579. """
  1580. ownerPartition = partition_map.get(PARTITION_ROLE.OWNER)[0]
  1581. if 'merchantId' not in ownerPartition:
  1582. return None
  1583. partitions = list(flatten(partition_map.values()))
  1584. rv = []
  1585. for partition in partitions:
  1586. if 'merchantId' in partition:
  1587. rv.append({
  1588. 'customerNum': partition['merchantId'],
  1589. 'amount': str(abs(RMB(partition['money'])))
  1590. })
  1591. if len(rv) <= 1:
  1592. return None
  1593. else:
  1594. return rv
  1595. class ManualPayApp(PayAppBase):
  1596. """
  1597. 客户线下打款后, 超级管理员建立充值记录
  1598. """
  1599. def __repr__(self):
  1600. return '<manual pay App>'
  1601. @property
  1602. def __valid_check__(self):
  1603. return True
  1604. def new_gateway(self, app_platform_type):
  1605. from apps.web.core.payment.manual import ManualPaymentGateway
  1606. return ManualPaymentGateway(self, app_platform_type)
  1607. @property
  1608. def pay_app_type(self):
  1609. return PayAppType.MANUAL
  1610. @property
  1611. def __gateway_key__(self):
  1612. _ = [
  1613. self.occupantId,
  1614. self.occupant.role
  1615. ]
  1616. return APP_KEY_DELIMITER.join(_)
  1617. @classmethod
  1618. def get_null_app(cls):
  1619. app = cls()
  1620. app.enable = True
  1621. app.valid = True
  1622. return app
  1623. @classmethod
  1624. def __from_gateway_key__(cls, occupant_id, tokens):
  1625. # type: (str, List)->cast(PayAppBase)
  1626. app = cls.get_null_app() # type: ManualPayApp
  1627. app.occupantId = occupant_id
  1628. try:
  1629. app.role = tokens[1]
  1630. except:
  1631. app.role = ROLE.agent
  1632. return app
  1633. class SwapPayApp(PayAppBase):
  1634. """
  1635. 互联互通支付APP
  1636. """
  1637. def __repr__(self):
  1638. return '<swap pay App>'
  1639. @property
  1640. def __valid_check__(self):
  1641. return True
  1642. def new_gateway(self, app_platform_type):
  1643. from apps.web.core.payment.manual import ManualPaymentGateway
  1644. return ManualPaymentGateway(self, app_platform_type)
  1645. @property
  1646. def pay_app_type(self):
  1647. return PayAppType.SWAP
  1648. @property
  1649. def __gateway_key__(self):
  1650. _ = [
  1651. self.occupantId,
  1652. self.occupant.role
  1653. ]
  1654. return APP_KEY_DELIMITER.join(_)
  1655. @classmethod
  1656. def get_null_app(cls):
  1657. app = cls()
  1658. app.enable = True
  1659. app.valid = True
  1660. return app
  1661. @classmethod
  1662. def __from_gateway_key__(cls, occupant_id, tokens):
  1663. # type: (str, List)->cast(PayAppBase)
  1664. app = cls.get_null_app() # type: SwapPayApp
  1665. app.occupantId = occupant_id
  1666. try:
  1667. app.role = tokens[1]
  1668. except:
  1669. app.role = ROLE.agent
  1670. return app
  1671. class WechatComponentApp(DynamicDocument):
  1672. appid = StringField(verbose_name='appid', default='', required=True)
  1673. secret = StringField(verbose_name="secretId", default='', required=True)
  1674. aesKey = StringField(verbose_name="aesKey", default='', required=True)
  1675. token = StringField(verbose_name="token", default='', required=True)
  1676. companyName = StringField(verbose_name="companyName", default='')
  1677. appName = StringField(verbose_name="appName", default='')
  1678. meta = {
  1679. 'collection': 'wechat_component_app'
  1680. }
  1681. class WechatAuthorizer(DynamicDocument):
  1682. appid = StringField(verbose_name='appid', default='', required=True)
  1683. appType = IntField(verbose_name='appType(0-mini,1-biz)', default=1)
  1684. serviceType = IntField(verbose_name='serviceType', default=2)
  1685. verifyInfo = IntField(verbose_name='verifyInfo')
  1686. nickName = StringField(verbose_name='appType', default='')
  1687. userName = StringField(verbose_name='appType', default='')
  1688. headImg = StringField(verbose_name='appType', default='')
  1689. qrcodeUrl = StringField(verbose_name='appType', default='')
  1690. principalName = StringField(verbose_name='appType', default='')
  1691. funcList = ListField(verbose_name='funcList', default=[])
  1692. appStatus = IntField(verbose_name='appStatus')
  1693. extra = DictField(verbose_name='extra', default={})
  1694. refreshToken = StringField(verbose_name='appType', default='')
  1695. updateTime = DateTimeField(default=datetime.datetime.now, verbose_name=u'授权时间')
  1696. dtAdded = DateTimeField(default=datetime.datetime.now, verbose_name=u'授权时间')
  1697. authState = StringField(verbose_name='authState', default='authorized')
  1698. meta = {
  1699. 'collection': 'wechat_authorizer'
  1700. }
  1701. REFRESH_TOKEN_KEY = 'refresh_token_{}'
  1702. @classmethod
  1703. def getAuthRecord(cls, appid):
  1704. token = serviceCache.get(cls.REFRESH_TOKEN_KEY.format(appid))
  1705. if not token:
  1706. authorizer = cls.objects(appid=appid).first()
  1707. if authorizer:
  1708. token = authorizer.refreshToken
  1709. serviceCache.set(cls.REFRESH_TOKEN_KEY.format(settings.WECHAT_3RD_APPID), token)
  1710. return token
  1711. @classmethod
  1712. def createOrUpdateAuthRecord(cls, payload):
  1713. payload.update({
  1714. 'authState': 'authorized',
  1715. 'updateTime': datetime.datetime.now()
  1716. })
  1717. cls.objects(appid=payload['appid']).update_one(upsert=True, **payload)
  1718. @classmethod
  1719. def deleteAuthRecord(cls, appid):
  1720. cls.objects(appid=appid).update_one(
  1721. appid=appid, authState='unauthorized', updateTime=datetime.datetime.now())
  1722. class WithdrawEntity(EmbeddedDocument):
  1723. wechatWithdrawApp = LazyReferenceField(document_type = WechatPayApp, default = None)
  1724. alipayWithdrawApp = LazyReferenceField(document_type = AliApp, default = None)
  1725. @property
  1726. def alipay_app(self):
  1727. if self.alipayWithdrawApp:
  1728. return self.alipayWithdrawApp.fetch()
  1729. else:
  1730. return None
  1731. @property
  1732. def wechat_app(self):
  1733. if self.wechatWithdrawApp:
  1734. return self.wechatWithdrawApp.fetch()
  1735. else:
  1736. return None
  1737. class BoundOpenInfo(EmbeddedDocument):
  1738. openId = StringField(required = True, default = '')
  1739. avatar = StringField(default = '')
  1740. sex = IntField(verbose_name = "性别", default = Const.USER_SEX.UNKNOWN)
  1741. nickname = StringField(verbose_name = "昵称", default = "")
  1742. def to_dict(self):
  1743. return {
  1744. 'openId': self.openId
  1745. }
  1746. def to_detail_dict(self):
  1747. return {
  1748. "openId": self.openId,
  1749. "avatar": self.avatar,
  1750. "sex": self.sex,
  1751. "nickname": self.nickname
  1752. }
  1753. class OfflineTask(Searchable):
  1754. _STATUS_MAP = Const.CELERY_TASK_RESULT_TRANSLATION
  1755. name = StringField(verbose_name = '任务名称')
  1756. type = StringField(verbose_name = '类型')
  1757. status = StringField(verbose_name = '状态', default = 'PENDING')
  1758. result = StringField(verbose_name = '执行结果详细信息', default = '')
  1759. link = StringField(verbose_name = '链接')
  1760. celery_task_id = StringField(verbose_name = 'celery侧uuid', default = '')
  1761. process_func_name = StringField(verbose_name = '任务的函数名')
  1762. role = StringField(verbose_name = '角色', choices = ROLE.choices())
  1763. ownerId = ObjectIdField(verbose_name = '创建者ID')
  1764. dateTimeAdded = DateTimeField(default = datetime.datetime.now, verbose_name = '任务生成时间')
  1765. finishedTime = DateTimeField(verbose_name = '完成时间', default = None)
  1766. search_fields = ('id', 'celery_task_id', 'name')
  1767. meta = {
  1768. 'collection': 'offline_task',
  1769. 'db_alias': 'logdata',
  1770. 'indexes': [
  1771. {
  1772. 'fields': ['ownerId', 'role']
  1773. }
  1774. ],
  1775. }
  1776. def to_dict(self):
  1777. return {
  1778. 'id': str(self.id),
  1779. 'name': self.name,
  1780. 'status': self.status,
  1781. 'result': self.result,
  1782. 'link': self.link,
  1783. 'celery_task_id': self.celery_task_id,
  1784. 'type': self.type,
  1785. 'startTime': self.dateTimeAdded,
  1786. 'finishedTime': self.finishedTime
  1787. }
  1788. @classmethod
  1789. def issue_export_report(cls, offline_task_name, process_func_name, task_type, userid, role, ext = 'xlsx'):
  1790. file_path = '{}reports/{}/{}.{}'.format(
  1791. settings.MEDIA_URL,
  1792. datetime.datetime.now().strftime("%Y%m%d"),
  1793. offline_task_name,
  1794. ext)
  1795. if settings.UPLOAD_REPORT_TO_OSS:
  1796. link = '{}{}'.format(settings.OSS_RESOURCE_URL, file_path)
  1797. else:
  1798. link = '{}{}?local=true'.format(settings.LOCAL_REPORT_URL, file_path)
  1799. offline_task = OfflineTask(
  1800. name = offline_task_name,
  1801. type = task_type,
  1802. process_func_name = process_func_name,
  1803. ownerId = userid,
  1804. role = role,
  1805. link = link)
  1806. offline_task.save()
  1807. return file_path[1:], offline_task
  1808. @classmethod
  1809. def issue(cls, offline_task_name, process_func_name, task_type, userid, role):
  1810. offline_task = OfflineTask(
  1811. name = offline_task_name,
  1812. type = task_type,
  1813. process_func_name = process_func_name,
  1814. ownerId = userid,
  1815. role = role)
  1816. offline_task.save()
  1817. return offline_task
  1818. class BankCard(Searchable):
  1819. """
  1820. TODO: 已经废弃.升级脚本暂时需要, 下个版本后删除
  1821. """
  1822. class AccountType(object):
  1823. PERSONAL = 'personal'
  1824. PUBLIC = 'public'
  1825. bankName = StringField(verbose_name = u"银行名称", required = True)
  1826. branchName = StringField(verbose_name = u"支行信息")
  1827. cardNo = StringField(verbose_name = u"银行卡号", required = True, unique = True)
  1828. cardType = StringField(verbose_name = u"卡类型", default = "debit")
  1829. remark = StringField(verbose_name = u"备注", default = "")
  1830. # category = StringField(verbose_name=u"所属类别,个人或者公司", default='individual')
  1831. province = StringField(verbose_name = u"省级别开户地址信息", default = "")
  1832. city = StringField(verbose_name = u"市级别开户地址信息", default = "")
  1833. district = StringField(verbose_name = u"区级别开户地址信息", default = "")
  1834. holderName = StringField(verbose_name = u"开户人姓名", required = True)
  1835. code = StringField(verbose_name = u"微信支持银行卡所对应的code(银行编号), 默认为0000表示不支持", default = "0000")
  1836. cnapsCode = StringField(verbose_name = u'支行联行号', default = '')
  1837. is_valid = BooleanField(verbose_name = u"是否是有效卡", default = True)
  1838. is_primary = BooleanField(verbose_name = u"优先选择", default = True)
  1839. manual = BooleanField(verbose_name = u"该卡是否仅手动提现", default = False)
  1840. accountType = StringField(verbose_name = u'卡类型(个人账号,对公账号)', default = AccountType.PERSONAL)
  1841. meta = {"collection": "bankcards", "db_alias": "default"}
  1842. class SystemSettings(Searchable):
  1843. key = StringField(verbose_name = u'设置名称')
  1844. value = DynamicField(verbose_name = u'设置参数')
  1845. desc = StringField(verbose_name = u'设置描述')
  1846. dateTimeAdded = DateTimeField(default = datetime.datetime.now, verbose_name = u'添加时间')
  1847. meta = {
  1848. 'collection': 'system_settings'
  1849. }
  1850. all_settings = {}
  1851. load_ready = False
  1852. @classmethod
  1853. def get_system_setting(cls, setting_name, default = None):
  1854. if not cls.load_ready:
  1855. cls.load_settings()
  1856. return cls.all_settings.get(setting_name, default)
  1857. @classmethod
  1858. def load_settings(cls):
  1859. cls.all_settings = {item.key: item.value for item in cls.objects.all()}
  1860. cls.load_ready = True
  1861. @classmethod
  1862. def reload_settings(cls):
  1863. logger.debug("reload system settings.")
  1864. cls.load_ready = False
  1865. cls.load_settings()
  1866. @classmethod
  1867. def set_setting(cls, key, value):
  1868. cls.objects(key=key).update_one(upsert=True, **{"value": value})
  1869. cls.all_settings[key] = value
  1870. @classmethod
  1871. def set_mem_setting(cls, key, value):
  1872. cls.all_settings[key] = value
  1873. @classmethod
  1874. def get_support_redpack_list(cls):
  1875. obj = cls.objects.filter(key='SUPPORT_REDPACK_LIST').first()
  1876. if obj:
  1877. return json_loads(obj.value)
  1878. else:
  1879. return []
  1880. @classmethod
  1881. def set_support_redpack_list(cls, dataList):
  1882. value = json_dumps(dataList)
  1883. return cls.objects.filter(key='SUPPORT_REDPACK_LIST').update(value=value)
  1884. @classmethod
  1885. def disable_alipay_ruhui(cls):
  1886. item = cls.objects.filter(key='DISABLE_REDPACK').first()
  1887. if not item:
  1888. vaule = {}
  1889. else:
  1890. vaule = item.value
  1891. return vaule.get('RUHUI', False)
  1892. @classmethod
  1893. def disable_alipay_laxin(cls):
  1894. item = cls.objects.filter(key='DISABLE_REDPACK').first()
  1895. if not item:
  1896. vaule = {}
  1897. else:
  1898. vaule = item.value
  1899. return vaule.get('LAXIN', False)
  1900. @classmethod
  1901. def get_system_setting_direct(cls, setting_name, default = None):
  1902. obj = cls.objects.filter(key = setting_name).first()
  1903. if obj:
  1904. return obj.value
  1905. else:
  1906. return default
  1907. class DriverCode(Searchable):
  1908. code = StringField(verbose_name = u'驱动编码', unique = True)
  1909. name = StringField(verbose_name = u'驱动名称', unique = True)
  1910. description = StringField(verbose_name = u'驱动描述')
  1911. createdTime = DateTimeField(default = datetime.datetime.now, verbose_name = u'添加进来的时间')
  1912. adapterFile = StringField(verbose_name = '业务处理文件名称', default = '')
  1913. adapterVer = StringField(verbose_name = '业务版本', default = '2.0') # 发生变化后,会自动化加载
  1914. adapter = StringField(verbose_name = '适配器类名称', default = '')
  1915. eventerFile = StringField(verbose_name = '事件处理文件名称', default = '')
  1916. eventerVer = StringField(verbose_name = '事件处理版本', default = '2.0') # 发生变化后,会自动化加载
  1917. features = DictField(verbose_name = "额外特征", default = {}) # 属于某个设备类型的特征
  1918. meta = {'collection': 'device_driver_code', 'db_alias': 'default'}
  1919. def __repr__(self):
  1920. return '<DriverCode code=%s>' % (self.code,)
  1921. def to_dict(self):
  1922. return {
  1923. 'code': self.code,
  1924. 'name': self.name,
  1925. 'features': self.features,
  1926. 'description': self.description
  1927. }
  1928. @classmethod
  1929. def get_driver_code(cls, code):
  1930. # type: (str)->dict
  1931. cache_key = lambda code: 'driverCode{}'.format(code)
  1932. if not code: return None
  1933. driverInfo = cache.get(cache_key(code))
  1934. if driverInfo:
  1935. return driverInfo
  1936. else:
  1937. obj = cls.objects(code = code).first() # type: DriverCode
  1938. if obj:
  1939. value = {
  1940. 'id': str(obj.id),
  1941. 'code': obj.code,
  1942. 'name': obj.name,
  1943. 'description': obj.description,
  1944. 'createdTime': obj.to_datetime_str(obj.createdTime),
  1945. 'adapterFile': obj.adapterFile,
  1946. 'eventerFile': obj.eventerFile,
  1947. 'features': dict(obj.features)
  1948. }
  1949. cache.set(cache_key(code), value)
  1950. return value
  1951. return None
  1952. class DriverAdapter(DynamicDocument):
  1953. adapterFile = StringField(verbose_name = '业务处理文件名称', unique = True)
  1954. adapterVer = StringField(verbose_name = '业务版本', default = '1.0')
  1955. adapter = StringField(verbose_name = '适配器类名称', default = '')
  1956. meta = {'collection': 'device_driver_adapter', 'db_alias': 'default'}
  1957. _lock = ThreadLock()
  1958. adapters = {}
  1959. def to_dict(self):
  1960. return {
  1961. 'adapterFile': self.adapterFile,
  1962. 'adapterVer': self.adapterVer,
  1963. 'adapter': self.adapter
  1964. }
  1965. @classmethod
  1966. def get_driver_adapter(cls, code, dev, online = True):
  1967. cache_key = lambda filename: 'driverAdapter{}'.format(filename)
  1968. adapter_file = None
  1969. if not online:
  1970. adapter_file = 'offline'
  1971. else:
  1972. driver_code_info = DriverCode.get_driver_code(code)
  1973. if driver_code_info:
  1974. adapter_file = driver_code_info['adapterFile']
  1975. if not adapter_file:
  1976. adapter_file = 'commonPulse'
  1977. adapter_info = cache.get(cache_key(adapter_file))
  1978. if not adapter_info:
  1979. adapter = cls.objects.get(adapterFile = adapter_file)
  1980. adapter_info = {
  1981. 'adapterFile': adapter.adapterFile,
  1982. 'adapterVer': adapter.adapterVer,
  1983. 'adapter': adapter.adapter
  1984. }
  1985. cache.set(cache_key(adapter_file), {
  1986. 'adapterFile': adapter_info['adapterFile'],
  1987. 'adapterVer': adapter_info['adapterVer'],
  1988. 'adapter': adapter_info['adapter']
  1989. })
  1990. logger.debug('[cache] load driver adapter info is: {}'.format(adapter_info))
  1991. mem_info = cls.adapters.get(adapter_file, None)
  1992. if mem_info and 'module' in mem_info and \
  1993. mem_info['adapterVer'] == adapter_info['adapterVer'] and \
  1994. mem_info['adapterFile'] == adapter_info['adapterFile'] and \
  1995. mem_info['adapter'] == adapter_info['adapter']:
  1996. adapter_module = cls.adapters[adapter_file]['module']
  1997. logger.debug('adapter module {} has loaded.'.format(adapter_module))
  1998. else:
  1999. try:
  2000. cls._lock.acquire_lock()
  2001. mem_info = cls.adapters.get(adapter_file, None)
  2002. if mem_info and 'module' in mem_info and \
  2003. mem_info['adapterVer'] == adapter_info['adapterVer'] and \
  2004. mem_info['adapterFile'] == adapter_info['adapterFile'] and \
  2005. mem_info['adapter'] == adapter_info['adapter']:
  2006. adapter_module = cls.adapters[adapter_file]['module']
  2007. logger.debug('adapter module {} has loaded.'.format(adapter_module))
  2008. else:
  2009. if adapter_file in cls.adapters:
  2010. mem_info = cls.adapters[adapter_file]
  2011. logger.info('device adapter changed, need reload, oldInfo=%s, newInfo=%s' % (
  2012. mem_info, adapter_info))
  2013. module_name = 'apps.web.core.adapter.{}'.format(adapter_info['adapterFile'])
  2014. adapter_module = import_module(module_name)
  2015. logger.info('finished import new module = {}'.format(adapter_module))
  2016. adapter_module = eval('reload({})'.format(module_name))
  2017. logger.info('finished reload new module = {}'.format(adapter_module))
  2018. adapter_info.update({'module': adapter_module})
  2019. cls.adapters[adapter_file] = adapter_info
  2020. else:
  2021. logger.info('loading device adapter, adapter info = {}'.format(adapter_info))
  2022. module_name = 'apps.web.core.adapter.{}'.format(adapter_file)
  2023. adapter_module = import_module(module_name)
  2024. logger.info('finished import new module = {}'.format(adapter_module))
  2025. adapter_info.update({'module': adapter_module})
  2026. cls.adapters[adapter_file] = adapter_info
  2027. finally:
  2028. cls._lock.release_lock()
  2029. return eval('adapter_module.%s(dev)' % (adapter_info['adapter']))
  2030. class DriverEventer(DynamicDocument):
  2031. eventerFile = StringField(verbose_name = '事件处理文件名称', unique = True)
  2032. eventerVer = StringField(verbose_name = '事件处理版本', default = '1.0') # 发生变化后,会自动化加载
  2033. meta = {'collection': 'device_driver_eventer', 'db_alias': 'default'}
  2034. _lock = ThreadLock()
  2035. eventers = {}
  2036. def to_dict(self):
  2037. return {
  2038. 'id': str(self.id),
  2039. 'eventerFile': self.eventerFile,
  2040. 'eventerVer': self.eventerVer
  2041. }
  2042. @classmethod
  2043. def get_driver_eventer(cls, code, device_adapter, online = True):
  2044. cache_key = lambda filename: 'driverEventer{}'.format(filename)
  2045. eventer_file = None
  2046. if not online:
  2047. eventer_file = 'offline'
  2048. else:
  2049. driver_code_info = DriverCode.get_driver_code(code)
  2050. if driver_code_info:
  2051. eventer_file = driver_code_info['eventerFile']
  2052. if not eventer_file:
  2053. if device_adapter.__class__.__name__ == 'CommonPulseAdapter':
  2054. eventer_file = 'commonPulse'
  2055. else:
  2056. eventer_file = 'dummy'
  2057. eventer_info = cache.get(cache_key(eventer_file))
  2058. if not eventer_info:
  2059. eventer = cls.objects.get(eventerFile = eventer_file)
  2060. eventer_info = {'eventerFile': eventer.eventerFile, 'eventerVer': eventer.eventerVer}
  2061. logger.debug('load driver eventer info is: {}'.format(eventer_info))
  2062. cache.set(cache_key(eventer_file),
  2063. {
  2064. 'eventerFile': eventer_info['eventerFile'],
  2065. 'eventerVer': eventer_info['eventerVer']
  2066. })
  2067. mem_info = cls.eventers.get(eventer_file, None)
  2068. if mem_info and 'module' in mem_info and mem_info['eventerVer'] == eventer_info['eventerVer'] and \
  2069. mem_info['eventerFile'] == eventer_info['eventerFile']:
  2070. eventer_module = mem_info['module']
  2071. logger.debug('eventer module {} has loaded.'.format(eventer_module))
  2072. else:
  2073. try:
  2074. cls._lock.acquire_lock()
  2075. mem_info = cls.eventers.get(eventer_file, None)
  2076. if mem_info and 'module' in mem_info and mem_info['eventerVer'] == eventer_info['eventerVer'] and \
  2077. mem_info['eventerFile'] == eventer_info['eventerFile']:
  2078. eventer_module = mem_info['module']
  2079. logger.debug('eventer module {} has loaded.'.format(eventer_module))
  2080. else:
  2081. if mem_info:
  2082. logger.info(
  2083. 'device eventer changed, need reload, oldInfo = %s, newInfo = %s' % (
  2084. mem_info, eventer_info))
  2085. module_name = 'apps.web.eventer.{}'.format(eventer_file)
  2086. eventer_module = import_module(module_name)
  2087. logger.info('finished import new eventer module = {}'.format(eventer_module))
  2088. eventer_module = eval('reload({})'.format(module_name))
  2089. logger.info('finished reload new eventer module = {}'.format(eventer_module))
  2090. eventer_info.update({'module': eventer_module})
  2091. cls.eventers[eventer_file] = eventer_info
  2092. else:
  2093. logger.info('loading device eventer, eventer info = {}'.format(eventer_info))
  2094. module_name = 'apps.web.eventer.{}'.format(eventer_file)
  2095. eventer_module = import_module(module_name)
  2096. logger.info('finished import new eventer module = {}'.format(eventer_module))
  2097. eventer_info.update({'module': eventer_module})
  2098. cls.eventers[eventer_file] = eventer_info
  2099. finally:
  2100. cls._lock.release_lock()
  2101. return eval('eventer_module.builder(device_adapter)')
  2102. class WechatServiceProvider(Searchable):
  2103. mchid = StringField(verbose_name = u'商户号', null = False)
  2104. apikey = StringField(verbose_name = u'api秘钥', default = '')
  2105. apiclient_cert_path = StringField(verbose_name = u'api证书路径', default = '')
  2106. apiclient_key_path = StringField(verbose_name = u'api证书秘钥路径', default = '')
  2107. apiclient_serial_number = StringField(verbose_name = u'api证书序列号', default = '')
  2108. sslCert = StringField(verbose_name = u'PERM证书内容', default = '')
  2109. sslKey = StringField(verbose_name = u'PKCS8 PERM私钥内容', default = '')
  2110. # v3的
  2111. apiKeyV3 = StringField(verbose_name = u'APIv3秘钥', default = '', db_field = "apikey_v3")
  2112. sslCertV3 = StringField(verbose_name = u"平台证书 用来加密敏感信息使用")
  2113. search_fields = ('mchid')
  2114. meta = {
  2115. 'indexes': [
  2116. {
  2117. 'fields': ['mchid'], 'unique': True
  2118. },
  2119. ],
  2120. 'collection': 'wechat_service_provider',
  2121. 'db_alias': 'default'
  2122. }
  2123. @property
  2124. def ssl_cert(self):
  2125. if not hasattr(self, '__ssl_cert__'):
  2126. try:
  2127. if self.sslCert.startswith('-----BEGIN'):
  2128. setattr(self, '__ssl_cert__', self.sslCert)
  2129. elif self.apiclient_cert_path.startswith('-----BEGIN'):
  2130. setattr(self, '__ssl_cert__', self.apiclient_cert_path)
  2131. else:
  2132. with open(self.apiclient_cert_path) as fp:
  2133. setattr(self, '__ssl_cert__', fp.read())
  2134. except Exception as e:
  2135. logger.error('{} ssl_cert exception = {}'.format(repr(self), e.message))
  2136. setattr(self, '__ssl_cert__', '')
  2137. return getattr(self, '__ssl_cert__')
  2138. @property
  2139. def ssl_key(self):
  2140. if not hasattr(self, '__ssl_key__'):
  2141. try:
  2142. if self.sslKey.startswith('-----BEGIN'):
  2143. setattr(self, '__ssl_key__', self.sslKey)
  2144. elif self.apiclient_key_path.startswith('-----BEGIN'):
  2145. setattr(self, '__ssl_key__', self.apiclient_key_path)
  2146. else:
  2147. with open(self.apiclient_key_path) as fp:
  2148. setattr(self, '__ssl_key__', fp.read())
  2149. except Exception as e:
  2150. logger.error('{} ssl_key exception = {}'.format(repr(self), e.message))
  2151. setattr(self, '__ssl_key__', '')
  2152. return getattr(self, '__ssl_key__')
  2153. class CustomerPayRelation(Searchable):
  2154. """
  2155. app 和使用者的关联表
  2156. 为 多对多的关系
  2157. """
  2158. ownerId = StringField(verbose_name=u"持有者ID")
  2159. ownerRole = StringField(verbose_name=u"持有者角色")
  2160. appId = StringField(verbose_name=u"app的ID")
  2161. appType = StringField(verbose_name=u"支付APP的种类")
  2162. active = BooleanField(verbose_name=u"是否处于使用状态", default=False)
  2163. isDelete = BooleanField(verbose_name=u"是否已经删除", default=False)
  2164. @classmethod
  2165. def add_relation(cls, customer, app):
  2166. rel = cls.objects.filter(ownerId=str(customer.id), appId=str(app.id))
  2167. if not rel:
  2168. rel = cls()
  2169. rel.ownerId = str(customer.id)
  2170. rel.ownerRole = str(customer.role)
  2171. rel.appId = str(app.id)
  2172. rel.appType = str(app.__class__.__name__)
  2173. return rel.save()
  2174. else:
  2175. return rel
  2176. def active_app(self):
  2177. """
  2178. 激活app 使之处于可用状态
  2179. """
  2180. self.active = True
  2181. return self.save()
  2182. def deactive_app(self):
  2183. """
  2184. 关闭app 使之处于不可用状态
  2185. """
  2186. self.active = False
  2187. return self.save()
  2188. def delete_relation(self):
  2189. """
  2190. 删除app
  2191. """
  2192. app = self.deactive_app()
  2193. app.isDelete = True
  2194. return app.save()