models.py 77 KB

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