models.py 77 KB

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