models.py 82 KB

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