models.py 77 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308
  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. agent = dealer.productAgent
  1407. app = cls()
  1408. app.role = ROLE.agent
  1409. app.occupantId = str(agent.id)
  1410. app.enable = True
  1411. app.valid = True
  1412. app.inhouse = True
  1413. app.debug = False
  1414. app.appName = cls.APP_NAME
  1415. return app
  1416. class ManualPayApp(PayAppBase):
  1417. """
  1418. 客户线下打款后, 超级管理员建立充值记录
  1419. """
  1420. def __repr__(self):
  1421. return '<manual pay App>'
  1422. @property
  1423. def __valid_check__(self):
  1424. return True
  1425. def new_gateway(self, app_platform_type):
  1426. from apps.web.core.payment.manual import ManualPaymentGateway
  1427. return ManualPaymentGateway(self, app_platform_type)
  1428. @property
  1429. def pay_app_type(self):
  1430. return PayAppType.MANUAL
  1431. @property
  1432. def __gateway_key__(self):
  1433. _ = [
  1434. self.occupantId,
  1435. self.occupant.role
  1436. ]
  1437. return APP_KEY_DELIMITER.join(_)
  1438. @classmethod
  1439. def get_null_app(cls):
  1440. app = cls()
  1441. app.enable = True
  1442. app.valid = True
  1443. return app
  1444. @classmethod
  1445. def __from_gateway_key__(cls, occupant_id, tokens):
  1446. # type: (str, List)->cast(PayAppBase)
  1447. app = cls.get_null_app() # type: ManualPayApp
  1448. app.occupantId = occupant_id
  1449. try:
  1450. app.role = tokens[1]
  1451. except:
  1452. app.role = ROLE.agent
  1453. return app
  1454. class WechatComponentApp(DynamicDocument):
  1455. appid = StringField(verbose_name='appid', default='', required=True)
  1456. secret = StringField(verbose_name="secretId", default='', required=True)
  1457. aesKey = StringField(verbose_name="aesKey", default='', required=True)
  1458. token = StringField(verbose_name="token", default='', required=True)
  1459. companyName = StringField(verbose_name="companyName", default='')
  1460. appName = StringField(verbose_name="appName", default='')
  1461. meta = {
  1462. 'collection': 'wechat_component_app'
  1463. }
  1464. class WechatAuthorizer(DynamicDocument):
  1465. appid = StringField(verbose_name='appid', default='', required=True)
  1466. appType = IntField(verbose_name='appType(0-mini,1-biz)', default=1)
  1467. serviceType = IntField(verbose_name='serviceType', default=2)
  1468. verifyInfo = IntField(verbose_name='verifyInfo')
  1469. nickName = StringField(verbose_name='appType', default='')
  1470. userName = StringField(verbose_name='appType', default='')
  1471. headImg = StringField(verbose_name='appType', default='')
  1472. qrcodeUrl = StringField(verbose_name='appType', default='')
  1473. principalName = StringField(verbose_name='appType', default='')
  1474. funcList = ListField(verbose_name='funcList', default=[])
  1475. appStatus = IntField(verbose_name='appStatus')
  1476. extra = DictField(verbose_name='extra', default={})
  1477. refreshToken = StringField(verbose_name='appType', default='')
  1478. updateTime = DateTimeField(default=datetime.datetime.now, verbose_name=u'授权时间')
  1479. dtAdded = DateTimeField(default=datetime.datetime.now, verbose_name=u'授权时间')
  1480. authState = StringField(verbose_name='authState', default='authorized')
  1481. meta = {
  1482. 'collection': 'wechat_authorizer'
  1483. }
  1484. REFRESH_TOKEN_KEY = 'refresh_token_{}'
  1485. @classmethod
  1486. def getAuthRecord(cls, appid):
  1487. token = serviceCache.get(cls.REFRESH_TOKEN_KEY.format(appid))
  1488. if not token:
  1489. authorizer = cls.objects(appid=appid).first()
  1490. if authorizer:
  1491. token = authorizer.refreshToken
  1492. serviceCache.set(cls.REFRESH_TOKEN_KEY.format(settings.WECHAT_3RD_APPID), token)
  1493. return token
  1494. @classmethod
  1495. def createOrUpdateAuthRecord(cls, payload):
  1496. payload.update({
  1497. 'authState': 'authorized',
  1498. 'updateTime': datetime.datetime.now()
  1499. })
  1500. cls.objects(appid=payload['appid']).update_one(upsert=True, **payload)
  1501. @classmethod
  1502. def deleteAuthRecord(cls, appid):
  1503. cls.objects(appid=appid).update_one(
  1504. appid=appid, authState='unauthorized', updateTime=datetime.datetime.now())
  1505. class WithdrawEntity(EmbeddedDocument):
  1506. wechatWithdrawApp = LazyReferenceField(document_type = WechatPayApp, default = None)
  1507. alipayWithdrawApp = LazyReferenceField(document_type = AliApp, default = None)
  1508. @property
  1509. def alipay_app(self):
  1510. if self.alipayWithdrawApp:
  1511. return self.alipayWithdrawApp.fetch()
  1512. else:
  1513. return None
  1514. @property
  1515. def wechat_app(self):
  1516. if self.wechatWithdrawApp:
  1517. return self.wechatWithdrawApp.fetch()
  1518. else:
  1519. return None
  1520. class BoundOpenInfo(EmbeddedDocument):
  1521. openId = StringField(required = True, default = '')
  1522. avatar = StringField(default = '')
  1523. sex = IntField(verbose_name = "性别", default = Const.USER_SEX.UNKNOWN)
  1524. nickname = StringField(verbose_name = "昵称", default = "")
  1525. def to_dict(self):
  1526. return {
  1527. 'openId': self.openId
  1528. }
  1529. def to_detail_dict(self):
  1530. return {
  1531. "openId": self.openId,
  1532. "avatar": self.avatar,
  1533. "sex": self.sex,
  1534. "nickname": self.nickname
  1535. }
  1536. class OfflineTask(Searchable):
  1537. _STATUS_MAP = Const.CELERY_TASK_RESULT_TRANSLATION
  1538. name = StringField(verbose_name = '任务名称')
  1539. type = StringField(verbose_name = '类型')
  1540. status = StringField(verbose_name = '状态', default = 'PENDING')
  1541. result = StringField(verbose_name = '执行结果详细信息', default = '')
  1542. link = StringField(verbose_name = '链接')
  1543. celery_task_id = StringField(verbose_name = 'celery侧uuid', default = '')
  1544. process_func_name = StringField(verbose_name = '任务的函数名')
  1545. role = StringField(verbose_name = '角色', choices = ROLE.choices())
  1546. ownerId = ObjectIdField(verbose_name = '创建者ID')
  1547. dateTimeAdded = DateTimeField(default = datetime.datetime.now, verbose_name = '任务生成时间')
  1548. finishedTime = DateTimeField(verbose_name = '完成时间', default = None)
  1549. search_fields = ('id', 'celery_task_id', 'name')
  1550. meta = {
  1551. 'collection': 'offline_task',
  1552. 'db_alias': 'logdata',
  1553. 'indexes': [
  1554. {
  1555. 'fields': ['ownerId', 'role']
  1556. }
  1557. ],
  1558. }
  1559. def to_dict(self):
  1560. return {
  1561. 'id': str(self.id),
  1562. 'name': self.name,
  1563. 'status': self.status,
  1564. 'result': self.result,
  1565. 'link': self.link,
  1566. 'celery_task_id': self.celery_task_id,
  1567. 'type': self.type,
  1568. 'startTime': self.dateTimeAdded,
  1569. 'finishedTime': self.finishedTime
  1570. }
  1571. @classmethod
  1572. def issue_export_report(cls, offline_task_name, process_func_name, task_type, userid, role, ext = 'xlsx'):
  1573. file_path = '{}reports/{}/{}.{}'.format(
  1574. settings.MEDIA_URL,
  1575. datetime.datetime.now().strftime("%Y%m%d"),
  1576. offline_task_name,
  1577. ext)
  1578. if settings.UPLOAD_REPORT_TO_OSS:
  1579. link = '{}{}'.format(settings.OSS_RESOURCE_URL, file_path)
  1580. else:
  1581. link = '{}{}?local=true'.format(settings.LOCAL_REPORT_URL, file_path)
  1582. offline_task = OfflineTask(
  1583. name = offline_task_name,
  1584. type = task_type,
  1585. process_func_name = process_func_name,
  1586. ownerId = userid,
  1587. role = role,
  1588. link = link)
  1589. offline_task.save()
  1590. return file_path[1:], offline_task
  1591. @classmethod
  1592. def issue(cls, offline_task_name, process_func_name, task_type, userid, role):
  1593. offline_task = OfflineTask(
  1594. name = offline_task_name,
  1595. type = task_type,
  1596. process_func_name = process_func_name,
  1597. ownerId = userid,
  1598. role = role)
  1599. offline_task.save()
  1600. return offline_task
  1601. class BankCard(Searchable):
  1602. """
  1603. TODO: 已经废弃.升级脚本暂时需要, 下个版本后删除
  1604. """
  1605. class AccountType(object):
  1606. PERSONAL = 'personal'
  1607. PUBLIC = 'public'
  1608. bankName = StringField(verbose_name = u"银行名称", required = True)
  1609. branchName = StringField(verbose_name = u"支行信息")
  1610. cardNo = StringField(verbose_name = u"银行卡号", required = True, unique = True)
  1611. cardType = StringField(verbose_name = u"卡类型", default = "debit")
  1612. remark = StringField(verbose_name = u"备注", default = "")
  1613. # category = StringField(verbose_name=u"所属类别,个人或者公司", default='individual')
  1614. province = StringField(verbose_name = u"省级别开户地址信息", default = "")
  1615. city = StringField(verbose_name = u"市级别开户地址信息", default = "")
  1616. district = StringField(verbose_name = u"区级别开户地址信息", default = "")
  1617. holderName = StringField(verbose_name = u"开户人姓名", required = True)
  1618. code = StringField(verbose_name = u"微信支持银行卡所对应的code(银行编号), 默认为0000表示不支持", default = "0000")
  1619. cnapsCode = StringField(verbose_name = u'支行联行号', default = '')
  1620. is_valid = BooleanField(verbose_name = u"是否是有效卡", default = True)
  1621. is_primary = BooleanField(verbose_name = u"优先选择", default = True)
  1622. manual = BooleanField(verbose_name = u"该卡是否仅手动提现", default = False)
  1623. accountType = StringField(verbose_name = u'卡类型(个人账号,对公账号)', default = AccountType.PERSONAL)
  1624. meta = {"collection": "bankcards", "db_alias": "default"}
  1625. class SystemSettings(Searchable):
  1626. key = StringField(verbose_name = u'设置名称')
  1627. value = DynamicField(verbose_name = u'设置参数')
  1628. desc = StringField(verbose_name = u'设置描述')
  1629. dateTimeAdded = DateTimeField(default = datetime.datetime.now, verbose_name = u'添加时间')
  1630. meta = {
  1631. 'collection': 'system_settings'
  1632. }
  1633. all_settings = {}
  1634. load_ready = False
  1635. @classmethod
  1636. def get_system_setting(cls, setting_name, default = None):
  1637. if not cls.load_ready:
  1638. cls.load_settings()
  1639. return cls.all_settings.get(setting_name, default)
  1640. @classmethod
  1641. def load_settings(cls):
  1642. cls.all_settings = {item.key: item.value for item in cls.objects.all()}
  1643. cls.load_ready = True
  1644. @classmethod
  1645. def reload_settings(cls):
  1646. logger.debug("reload system settings.")
  1647. cls.load_ready = False
  1648. cls.load_settings()
  1649. @classmethod
  1650. def set_setting(cls, key, value):
  1651. cls.objects(key=key).update_one(upsert=True, **{"value": value})
  1652. cls.all_settings[key] = value
  1653. @classmethod
  1654. def set_mem_setting(cls, key, value):
  1655. cls.all_settings[key] = value
  1656. @classmethod
  1657. def get_support_redpack_list(cls):
  1658. obj = cls.objects.filter(key='SUPPORT_REDPACK_LIST').first()
  1659. if obj:
  1660. return json_loads(obj.value)
  1661. else:
  1662. return []
  1663. @classmethod
  1664. def set_support_redpack_list(cls, dataList):
  1665. value = json_dumps(dataList)
  1666. return cls.objects.filter(key='SUPPORT_REDPACK_LIST').update(value=value)
  1667. @classmethod
  1668. def disable_alipay_ruhui(cls):
  1669. item = cls.objects.filter(key='DISABLE_REDPACK').first()
  1670. if not item:
  1671. vaule = {}
  1672. else:
  1673. vaule = item.value
  1674. return vaule.get('RUHUI', False)
  1675. @classmethod
  1676. def disable_alipay_laxin(cls):
  1677. item = cls.objects.filter(key='DISABLE_REDPACK').first()
  1678. if not item:
  1679. vaule = {}
  1680. else:
  1681. vaule = item.value
  1682. return vaule.get('LAXIN', False)
  1683. @classmethod
  1684. def get_system_setting_direct(cls, setting_name, default = None):
  1685. obj = cls.objects.filter(key = setting_name).first()
  1686. if obj:
  1687. return obj.value
  1688. else:
  1689. return default
  1690. class DriverCode(Searchable):
  1691. code = StringField(verbose_name = u'驱动编码', unique = True)
  1692. name = StringField(verbose_name = u'驱动名称', unique = True)
  1693. description = StringField(verbose_name = u'驱动描述')
  1694. createdTime = DateTimeField(default = datetime.datetime.now, verbose_name = u'添加进来的时间')
  1695. adapterFile = StringField(verbose_name = '业务处理文件名称', default = '')
  1696. adapterVer = StringField(verbose_name = '业务版本', default = '2.0') # 发生变化后,会自动化加载
  1697. adapter = StringField(verbose_name = '适配器类名称', default = '')
  1698. eventerFile = StringField(verbose_name = '事件处理文件名称', default = '')
  1699. eventerVer = StringField(verbose_name = '事件处理版本', default = '2.0') # 发生变化后,会自动化加载
  1700. features = DictField(verbose_name = "额外特征", default = {}) # 属于某个设备类型的特征
  1701. meta = {'collection': 'device_driver_code', 'db_alias': 'default'}
  1702. def __repr__(self):
  1703. return '<DriverCode code=%s>' % (self.code,)
  1704. def to_dict(self):
  1705. return {
  1706. 'code': self.code,
  1707. 'name': self.name,
  1708. 'features': self.features,
  1709. 'description': self.description
  1710. }
  1711. @classmethod
  1712. def get_driver_code(cls, code):
  1713. # type: (str)->dict
  1714. cache_key = lambda code: 'driverCode{}'.format(code)
  1715. if not code: return None
  1716. driverInfo = cache.get(cache_key(code))
  1717. if driverInfo:
  1718. return driverInfo
  1719. else:
  1720. obj = cls.objects(code = code).first() # type: DriverCode
  1721. if obj:
  1722. value = {
  1723. 'id': str(obj.id),
  1724. 'code': obj.code,
  1725. 'name': obj.name,
  1726. 'description': obj.description,
  1727. 'createdTime': obj.to_datetime_str(obj.createdTime),
  1728. 'adapterFile': obj.adapterFile,
  1729. 'eventerFile': obj.eventerFile,
  1730. 'features': dict(obj.features)
  1731. }
  1732. cache.set(cache_key(code), value)
  1733. return value
  1734. return None
  1735. class DriverAdapter(DynamicDocument):
  1736. adapterFile = StringField(verbose_name = '业务处理文件名称', unique = True)
  1737. adapterVer = StringField(verbose_name = '业务版本', default = '1.0')
  1738. adapter = StringField(verbose_name = '适配器类名称', default = '')
  1739. meta = {'collection': 'device_driver_adapter', 'db_alias': 'default'}
  1740. _lock = ThreadLock()
  1741. adapters = {}
  1742. def to_dict(self):
  1743. return {
  1744. 'adapterFile': self.adapterFile,
  1745. 'adapterVer': self.adapterVer,
  1746. 'adapter': self.adapter
  1747. }
  1748. @classmethod
  1749. def get_driver_adapter(cls, code, dev, online = True):
  1750. cache_key = lambda filename: 'driverAdapter{}'.format(filename)
  1751. adapter_file = None
  1752. if not online:
  1753. adapter_file = 'offline'
  1754. else:
  1755. driver_code_info = DriverCode.get_driver_code(code)
  1756. if driver_code_info:
  1757. adapter_file = driver_code_info['adapterFile']
  1758. if not adapter_file:
  1759. adapter_file = 'commonPulse'
  1760. adapter_info = cache.get(cache_key(adapter_file))
  1761. if not adapter_info:
  1762. adapter = cls.objects.get(adapterFile = adapter_file)
  1763. adapter_info = {
  1764. 'adapterFile': adapter.adapterFile,
  1765. 'adapterVer': adapter.adapterVer,
  1766. 'adapter': adapter.adapter
  1767. }
  1768. cache.set(cache_key(adapter_file), {
  1769. 'adapterFile': adapter_info['adapterFile'],
  1770. 'adapterVer': adapter_info['adapterVer'],
  1771. 'adapter': adapter_info['adapter']
  1772. })
  1773. logger.debug('[cache] load driver adapter info is: {}'.format(adapter_info))
  1774. mem_info = cls.adapters.get(adapter_file, None)
  1775. if mem_info and 'module' in mem_info and \
  1776. mem_info['adapterVer'] == adapter_info['adapterVer'] and \
  1777. mem_info['adapterFile'] == adapter_info['adapterFile'] and \
  1778. mem_info['adapter'] == adapter_info['adapter']:
  1779. adapter_module = cls.adapters[adapter_file]['module']
  1780. logger.debug('adapter module {} has loaded.'.format(adapter_module))
  1781. else:
  1782. try:
  1783. cls._lock.acquire_lock()
  1784. mem_info = cls.adapters.get(adapter_file, None)
  1785. if mem_info and 'module' in mem_info and \
  1786. mem_info['adapterVer'] == adapter_info['adapterVer'] and \
  1787. mem_info['adapterFile'] == adapter_info['adapterFile'] and \
  1788. mem_info['adapter'] == adapter_info['adapter']:
  1789. adapter_module = cls.adapters[adapter_file]['module']
  1790. logger.debug('adapter module {} has loaded.'.format(adapter_module))
  1791. else:
  1792. if adapter_file in cls.adapters:
  1793. mem_info = cls.adapters[adapter_file]
  1794. logger.info('device adapter changed, need reload, oldInfo=%s, newInfo=%s' % (
  1795. mem_info, adapter_info))
  1796. module_name = 'apps.web.core.adapter.{}'.format(adapter_info['adapterFile'])
  1797. adapter_module = import_module(module_name)
  1798. logger.info('finished import new module = {}'.format(adapter_module))
  1799. adapter_module = eval('reload({})'.format(module_name))
  1800. logger.info('finished reload new module = {}'.format(adapter_module))
  1801. adapter_info.update({'module': adapter_module})
  1802. cls.adapters[adapter_file] = adapter_info
  1803. else:
  1804. logger.info('loading device adapter, adapter info = {}'.format(adapter_info))
  1805. module_name = 'apps.web.core.adapter.{}'.format(adapter_file)
  1806. adapter_module = import_module(module_name)
  1807. logger.info('finished import new module = {}'.format(adapter_module))
  1808. adapter_info.update({'module': adapter_module})
  1809. cls.adapters[adapter_file] = adapter_info
  1810. finally:
  1811. cls._lock.release_lock()
  1812. return eval('adapter_module.%s(dev)' % (adapter_info['adapter']))
  1813. class DriverEventer(DynamicDocument):
  1814. eventerFile = StringField(verbose_name = '事件处理文件名称', unique = True)
  1815. eventerVer = StringField(verbose_name = '事件处理版本', default = '1.0') # 发生变化后,会自动化加载
  1816. meta = {'collection': 'device_driver_eventer', 'db_alias': 'default'}
  1817. _lock = ThreadLock()
  1818. eventers = {}
  1819. def to_dict(self):
  1820. return {
  1821. 'id': str(self.id),
  1822. 'eventerFile': self.eventerFile,
  1823. 'eventerVer': self.eventerVer
  1824. }
  1825. @classmethod
  1826. def get_driver_eventer(cls, code, device_adapter, online = True):
  1827. cache_key = lambda filename: 'driverEventer{}'.format(filename)
  1828. eventer_file = None
  1829. if not online:
  1830. eventer_file = 'offline'
  1831. else:
  1832. driver_code_info = DriverCode.get_driver_code(code)
  1833. if driver_code_info:
  1834. eventer_file = driver_code_info['eventerFile']
  1835. if not eventer_file:
  1836. if device_adapter.__class__.__name__ == 'CommonPulseAdapter':
  1837. eventer_file = 'commonPulse'
  1838. else:
  1839. eventer_file = 'dummy'
  1840. eventer_info = cache.get(cache_key(eventer_file))
  1841. if not eventer_info:
  1842. eventer = cls.objects.get(eventerFile = eventer_file)
  1843. eventer_info = {'eventerFile': eventer.eventerFile, 'eventerVer': eventer.eventerVer}
  1844. logger.debug('load driver eventer info is: {}'.format(eventer_info))
  1845. cache.set(cache_key(eventer_file),
  1846. {
  1847. 'eventerFile': eventer_info['eventerFile'],
  1848. 'eventerVer': eventer_info['eventerVer']
  1849. })
  1850. mem_info = cls.eventers.get(eventer_file, None)
  1851. if mem_info and 'module' in mem_info and mem_info['eventerVer'] == eventer_info['eventerVer'] and \
  1852. mem_info['eventerFile'] == eventer_info['eventerFile']:
  1853. eventer_module = mem_info['module']
  1854. logger.debug('eventer module {} has loaded.'.format(eventer_module))
  1855. else:
  1856. try:
  1857. cls._lock.acquire_lock()
  1858. mem_info = cls.eventers.get(eventer_file, None)
  1859. if mem_info and 'module' in mem_info and mem_info['eventerVer'] == eventer_info['eventerVer'] and \
  1860. mem_info['eventerFile'] == eventer_info['eventerFile']:
  1861. eventer_module = mem_info['module']
  1862. logger.debug('eventer module {} has loaded.'.format(eventer_module))
  1863. else:
  1864. if mem_info:
  1865. logger.info(
  1866. 'device eventer changed, need reload, oldInfo = %s, newInfo = %s' % (
  1867. mem_info, eventer_info))
  1868. module_name = 'apps.web.eventer.{}'.format(eventer_file)
  1869. eventer_module = import_module(module_name)
  1870. logger.info('finished import new eventer module = {}'.format(eventer_module))
  1871. eventer_module = eval('reload({})'.format(module_name))
  1872. logger.info('finished reload new eventer module = {}'.format(eventer_module))
  1873. eventer_info.update({'module': eventer_module})
  1874. cls.eventers[eventer_file] = eventer_info
  1875. else:
  1876. logger.info('loading device eventer, eventer info = {}'.format(eventer_info))
  1877. module_name = 'apps.web.eventer.{}'.format(eventer_file)
  1878. eventer_module = import_module(module_name)
  1879. logger.info('finished import new eventer module = {}'.format(eventer_module))
  1880. eventer_info.update({'module': eventer_module})
  1881. cls.eventers[eventer_file] = eventer_info
  1882. finally:
  1883. cls._lock.release_lock()
  1884. return eval('eventer_module.builder(device_adapter)')
  1885. class CustomerPayRelation(Searchable):
  1886. """
  1887. app 和使用者的关联表
  1888. 为 多对多的关系
  1889. """
  1890. ownerId = StringField(verbose_name=u"持有者ID")
  1891. ownerRole = StringField(verbose_name=u"持有者角色")
  1892. appId = StringField(verbose_name=u"app的ID")
  1893. appType = StringField(verbose_name=u"支付APP的种类")
  1894. active = BooleanField(verbose_name=u"是否处于使用状态", default=False)
  1895. isDelete = BooleanField(verbose_name=u"是否已经删除", default=False)
  1896. @classmethod
  1897. def add_relation(cls, customer, app):
  1898. rel = cls.objects.filter(ownerId=str(customer.id), appId=str(app.id))
  1899. if not rel:
  1900. rel = cls()
  1901. rel.ownerId = str(customer.id)
  1902. rel.ownerRole = str(customer.role)
  1903. rel.appId = str(app.id)
  1904. rel.appType = str(app.__class__.__name__)
  1905. return rel.save()
  1906. else:
  1907. return rel
  1908. def active_app(self):
  1909. """
  1910. 激活app 使之处于可用状态
  1911. """
  1912. self.active = True
  1913. return self.save()
  1914. def deactive_app(self):
  1915. """
  1916. 关闭app 使之处于不可用状态
  1917. """
  1918. self.active = False
  1919. return self.save()
  1920. def delete_relation(self):
  1921. """
  1922. 删除app
  1923. """
  1924. app = self.deactive_app()
  1925. app.isDelete = True
  1926. return app.save()