models.py 82 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460
  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. @classmethod
  1397. def __from_gateway_key__(cls, occupant_id, tokens):
  1398. # type: (str, List)->cast(PayAppBase, None)
  1399. appid = tokens[0]
  1400. app = cls.objects(appid = appid).first() # type: AliApp
  1401. if not app:
  1402. raise UserServerException(u'系统配置错误,请联系平台客服(10005)')
  1403. app.occupantId = occupant_id
  1404. try:
  1405. app.role = tokens[1]
  1406. except:
  1407. app.role = ROLE.agent
  1408. return app
  1409. @property
  1410. def __gateway_key__(self):
  1411. _ = [
  1412. self.occupantId,
  1413. self.appid,
  1414. self.occupant.role
  1415. ]
  1416. return APP_KEY_DELIMITER.join(_)
  1417. @property
  1418. def client(self):
  1419. if hasattr(self, '__client__'):
  1420. return getattr(self, '__client__')
  1421. _client = self.ISVApp.client
  1422. setattr(self, '__client__', _client)
  1423. return _client
  1424. class PlatformAppBase(PayAppBase):
  1425. """
  1426. 平台记账APP
  1427. """
  1428. meta = {
  1429. 'abstract': True
  1430. }
  1431. APP_NAME = ''
  1432. @property
  1433. def __valid_check__(self):
  1434. return True
  1435. @property
  1436. def __gateway_key__(self):
  1437. _ = [
  1438. settings.MY_PRIMARY_AGENT_ID,
  1439. ROLE.agent
  1440. ]
  1441. return APP_KEY_DELIMITER.join(_)
  1442. @classmethod
  1443. def get_app(cls):
  1444. app = cls()
  1445. app.role = ROLE.agent
  1446. app.occupantId = settings.MY_PRIMARY_AGENT_ID
  1447. app.enable = True
  1448. app.valid = True
  1449. app.inhouse = True
  1450. app.debug = False
  1451. app.companyName = u'武汉大源科技有限公司'
  1452. app.appName = cls.APP_NAME
  1453. return app
  1454. @classmethod
  1455. def __from_gateway_key__(cls, occupant_id, tokens):
  1456. # type: (str, List)->cast(PayAppBase)
  1457. assert occupant_id == settings.MY_PRIMARY_AGENT_ID, u'非法GatewayKey'
  1458. return cls.get_app()
  1459. class PlatformPromotionApp(PlatformAppBase):
  1460. """
  1461. 用于平台记账后 分账给其他人, 例如红包受益 广告收益等 分给经销商
  1462. """
  1463. meta = {
  1464. 'abstract': True
  1465. }
  1466. APP_NAME = u'平台补贴'
  1467. def __repr__(self):
  1468. return '<platform promotion App>'
  1469. def new_gateway(self, app_platform_type):
  1470. from apps.web.core.payment.platform import PlatformPromotionPaymentGateway
  1471. return PlatformPromotionPaymentGateway(self, app_platform_type)
  1472. @property
  1473. def pay_app_type(self):
  1474. return PayAppType.PLATFORM_PROMOTION
  1475. class PlatformReconcileApp(PlatformAppBase):
  1476. """
  1477. 平台对账后建立订单分账
  1478. """
  1479. meta = {
  1480. 'abstract': True
  1481. }
  1482. APP_NAME = u'平台对账'
  1483. def __repr__(self):
  1484. return '<platform reconcile App>'
  1485. def new_gateway(self, app_platform_type):
  1486. from apps.web.core.payment.platform import PlatformReconcilePaymentGateway
  1487. return PlatformReconcilePaymentGateway(self, app_platform_type)
  1488. @property
  1489. def pay_app_type(self):
  1490. return PayAppType.PLATFORM_RECONCILE
  1491. class PlatformWalletApp(PlatformPromotionApp):
  1492. """
  1493. 用于使用经销商余额(平台资金池)进行SIM卡支付
  1494. """
  1495. meta = {
  1496. 'abstract': True
  1497. }
  1498. def __repr__(self):
  1499. return '<platform wallet App>'
  1500. def new_gateway(self, app_platform_type):
  1501. from apps.web.core.payment.platform import PlatformWalletPaymentGateway
  1502. return PlatformWalletPaymentGateway(self, app_platform_type)
  1503. @property
  1504. def pay_app_type(self):
  1505. return PayAppType.PLATFORM_WALLET
  1506. class LedgerConsumeApp(PayAppBase):
  1507. meta = {
  1508. 'abstract': True
  1509. }
  1510. APP_NAME = u'消费分账'
  1511. @property
  1512. def pay_app_type(self):
  1513. return PayAppType.LEDGER_CONSUME
  1514. def new_gateway(self, app_platform_type):
  1515. from apps.web.core.payment.consume import LedgerConsumePaymentGateway
  1516. return LedgerConsumePaymentGateway(self, app_platform_type)
  1517. @classmethod
  1518. def get_app(cls, dealer): # type:(Dealer) -> LedgerConsumeApp
  1519. app = cls()
  1520. app.role = ROLE.agent
  1521. app.occupantId = settings.MY_PRIMARY_AGENT_ID
  1522. app.enable = True
  1523. app.valid = True
  1524. app.inhouse = True
  1525. app.debug = False
  1526. app.appName = cls.APP_NAME
  1527. return app
  1528. class ManualPayApp(PayAppBase):
  1529. """
  1530. 客户线下打款后, 超级管理员建立充值记录
  1531. """
  1532. def __repr__(self):
  1533. return '<manual pay App>'
  1534. @property
  1535. def __valid_check__(self):
  1536. return True
  1537. def new_gateway(self, app_platform_type):
  1538. from apps.web.core.payment.manual import ManualPaymentGateway
  1539. return ManualPaymentGateway(self, app_platform_type)
  1540. @property
  1541. def pay_app_type(self):
  1542. return PayAppType.MANUAL
  1543. @property
  1544. def __gateway_key__(self):
  1545. _ = [
  1546. self.occupantId,
  1547. self.occupant.role
  1548. ]
  1549. return APP_KEY_DELIMITER.join(_)
  1550. @classmethod
  1551. def get_null_app(cls):
  1552. app = cls()
  1553. app.enable = True
  1554. app.valid = True
  1555. return app
  1556. @classmethod
  1557. def __from_gateway_key__(cls, occupant_id, tokens):
  1558. # type: (str, List)->cast(PayAppBase)
  1559. app = cls.get_null_app() # type: ManualPayApp
  1560. app.occupantId = occupant_id
  1561. try:
  1562. app.role = tokens[1]
  1563. except:
  1564. app.role = ROLE.agent
  1565. return app
  1566. class WechatComponentApp(DynamicDocument):
  1567. appid = StringField(verbose_name='appid', default='', required=True)
  1568. secret = StringField(verbose_name="secretId", default='', required=True)
  1569. aesKey = StringField(verbose_name="aesKey", default='', required=True)
  1570. token = StringField(verbose_name="token", default='', required=True)
  1571. companyName = StringField(verbose_name="companyName", default='')
  1572. appName = StringField(verbose_name="appName", default='')
  1573. meta = {
  1574. 'collection': 'wechat_component_app'
  1575. }
  1576. class WechatAuthorizer(DynamicDocument):
  1577. appid = StringField(verbose_name='appid', default='', required=True)
  1578. appType = IntField(verbose_name='appType(0-mini,1-biz)', default=1)
  1579. serviceType = IntField(verbose_name='serviceType', default=2)
  1580. verifyInfo = IntField(verbose_name='verifyInfo')
  1581. nickName = StringField(verbose_name='appType', default='')
  1582. userName = StringField(verbose_name='appType', default='')
  1583. headImg = StringField(verbose_name='appType', default='')
  1584. qrcodeUrl = StringField(verbose_name='appType', default='')
  1585. principalName = StringField(verbose_name='appType', default='')
  1586. funcList = ListField(verbose_name='funcList', default=[])
  1587. appStatus = IntField(verbose_name='appStatus')
  1588. extra = DictField(verbose_name='extra', default={})
  1589. refreshToken = StringField(verbose_name='appType', default='')
  1590. updateTime = DateTimeField(default=datetime.datetime.now, verbose_name=u'授权时间')
  1591. dtAdded = DateTimeField(default=datetime.datetime.now, verbose_name=u'授权时间')
  1592. authState = StringField(verbose_name='authState', default='authorized')
  1593. meta = {
  1594. 'collection': 'wechat_authorizer'
  1595. }
  1596. REFRESH_TOKEN_KEY = 'refresh_token_{}'
  1597. @classmethod
  1598. def getAuthRecord(cls, appid):
  1599. token = serviceCache.get(cls.REFRESH_TOKEN_KEY.format(appid))
  1600. if not token:
  1601. authorizer = cls.objects(appid=appid).first()
  1602. if authorizer:
  1603. token = authorizer.refreshToken
  1604. serviceCache.set(cls.REFRESH_TOKEN_KEY.format(settings.WECHAT_3RD_APPID), token)
  1605. return token
  1606. @classmethod
  1607. def createOrUpdateAuthRecord(cls, payload):
  1608. payload.update({
  1609. 'authState': 'authorized',
  1610. 'updateTime': datetime.datetime.now()
  1611. })
  1612. cls.objects(appid=payload['appid']).update_one(upsert=True, **payload)
  1613. @classmethod
  1614. def deleteAuthRecord(cls, appid):
  1615. cls.objects(appid=appid).update_one(
  1616. appid=appid, authState='unauthorized', updateTime=datetime.datetime.now())
  1617. class WithdrawEntity(EmbeddedDocument):
  1618. wechatWithdrawApp = LazyReferenceField(document_type = WechatPayApp, default = None)
  1619. alipayWithdrawApp = LazyReferenceField(document_type = AliApp, default = None)
  1620. aliWithdrawApp = GenericLazyReferenceField(choices = [AliApp, AliFundAccountBookApp], default = None)
  1621. @property
  1622. def alipay_app(self):
  1623. if self.aliWithdrawApp:
  1624. return self.aliWithdrawApp.fetch()
  1625. elif self.alipayWithdrawApp:
  1626. return self.alipayWithdrawApp.fetch()
  1627. else:
  1628. return None
  1629. @property
  1630. def wechat_app(self):
  1631. if self.wechatWithdrawApp:
  1632. return self.wechatWithdrawApp.fetch()
  1633. else:
  1634. return None
  1635. class BoundOpenInfo(EmbeddedDocument):
  1636. openId = StringField(required = True, default = '')
  1637. avatar = StringField(default = '')
  1638. sex = IntField(verbose_name = "性别", default = Const.USER_SEX.UNKNOWN)
  1639. nickname = StringField(verbose_name = "昵称", default = "")
  1640. def to_dict(self):
  1641. return {
  1642. 'openId': self.openId
  1643. }
  1644. def to_detail_dict(self):
  1645. return {
  1646. "openId": self.openId,
  1647. "avatar": self.avatar,
  1648. "sex": self.sex,
  1649. "nickname": self.nickname
  1650. }
  1651. class OfflineTask(Searchable):
  1652. _STATUS_MAP = Const.CELERY_TASK_RESULT_TRANSLATION
  1653. name = StringField(verbose_name = '任务名称')
  1654. type = StringField(verbose_name = '类型')
  1655. status = StringField(verbose_name = '状态', default = 'PENDING')
  1656. result = StringField(verbose_name = '执行结果详细信息', default = '')
  1657. link = StringField(verbose_name = '链接')
  1658. celery_task_id = StringField(verbose_name = 'celery侧uuid', default = '')
  1659. process_func_name = StringField(verbose_name = '任务的函数名')
  1660. role = StringField(verbose_name = '角色', choices = ROLE.choices())
  1661. ownerId = ObjectIdField(verbose_name = '创建者ID')
  1662. dateTimeAdded = DateTimeField(default = datetime.datetime.now, verbose_name = '任务生成时间')
  1663. finishedTime = DateTimeField(verbose_name = '完成时间', default = None)
  1664. search_fields = ('id', 'celery_task_id', 'name')
  1665. meta = {
  1666. 'collection': 'offline_task',
  1667. 'db_alias': 'logdata',
  1668. 'indexes': [
  1669. {
  1670. 'fields': ['ownerId', 'role']
  1671. }
  1672. ],
  1673. }
  1674. def to_dict(self):
  1675. return {
  1676. 'id': str(self.id),
  1677. 'name': self.name,
  1678. 'status': self.status,
  1679. 'result': self.result,
  1680. 'link': self.link,
  1681. 'celery_task_id': self.celery_task_id,
  1682. 'type': self.type,
  1683. 'startTime': self.dateTimeAdded,
  1684. 'finishedTime': self.finishedTime
  1685. }
  1686. @classmethod
  1687. def issue_export_report(cls, offline_task_name, process_func_name, task_type, userid, role, ext = 'xlsx'):
  1688. file_path = '{}reports/{}/{}.{}'.format(
  1689. settings.MEDIA_URL,
  1690. datetime.datetime.now().strftime("%Y%m%d"),
  1691. offline_task_name,
  1692. ext)
  1693. if settings.UPLOAD_REPORT_TO_OSS:
  1694. link = '{}{}'.format(settings.OSS_RESOURCE_URL, file_path)
  1695. else:
  1696. link = '{}{}?local=true'.format(settings.LOCAL_REPORT_URL, file_path)
  1697. offline_task = OfflineTask(
  1698. name = offline_task_name,
  1699. type = task_type,
  1700. process_func_name = process_func_name,
  1701. ownerId = userid,
  1702. role = role,
  1703. link = link)
  1704. offline_task.save()
  1705. return file_path[1:], offline_task
  1706. @classmethod
  1707. def issue(cls, offline_task_name, process_func_name, task_type, userid, role):
  1708. offline_task = OfflineTask(
  1709. name = offline_task_name,
  1710. type = task_type,
  1711. process_func_name = process_func_name,
  1712. ownerId = userid,
  1713. role = role)
  1714. offline_task.save()
  1715. return offline_task
  1716. class BankCard(Searchable):
  1717. """
  1718. TODO: 已经废弃.升级脚本暂时需要, 下个版本后删除
  1719. """
  1720. class AccountType(object):
  1721. PERSONAL = 'personal'
  1722. PUBLIC = 'public'
  1723. bankName = StringField(verbose_name = u"银行名称", required = True)
  1724. branchName = StringField(verbose_name = u"支行信息")
  1725. cardNo = StringField(verbose_name = u"银行卡号", required = True, unique = True)
  1726. cardType = StringField(verbose_name = u"卡类型", default = "debit")
  1727. remark = StringField(verbose_name = u"备注", default = "")
  1728. # category = StringField(verbose_name=u"所属类别,个人或者公司", default='individual')
  1729. province = StringField(verbose_name = u"省级别开户地址信息", default = "")
  1730. city = StringField(verbose_name = u"市级别开户地址信息", default = "")
  1731. district = StringField(verbose_name = u"区级别开户地址信息", default = "")
  1732. holderName = StringField(verbose_name = u"开户人姓名", required = True)
  1733. code = StringField(verbose_name = u"微信支持银行卡所对应的code(银行编号), 默认为0000表示不支持", default = "0000")
  1734. cnapsCode = StringField(verbose_name = u'支行联行号', default = '')
  1735. is_valid = BooleanField(verbose_name = u"是否是有效卡", default = True)
  1736. is_primary = BooleanField(verbose_name = u"优先选择", default = True)
  1737. manual = BooleanField(verbose_name = u"该卡是否仅手动提现", default = False)
  1738. accountType = StringField(verbose_name = u'卡类型(个人账号,对公账号)', default = AccountType.PERSONAL)
  1739. meta = {"collection": "bankcards", "db_alias": "default"}
  1740. class SystemSettings(Searchable):
  1741. key = StringField(verbose_name = u'设置名称')
  1742. value = DynamicField(verbose_name = u'设置参数')
  1743. desc = StringField(verbose_name = u'设置描述')
  1744. dateTimeAdded = DateTimeField(default = datetime.datetime.now, verbose_name = u'添加时间')
  1745. meta = {
  1746. 'collection': 'system_settings'
  1747. }
  1748. all_settings = {}
  1749. load_ready = False
  1750. @classmethod
  1751. def get_system_setting(cls, setting_name, default = None):
  1752. if not cls.load_ready:
  1753. cls.load_settings()
  1754. return cls.all_settings.get(setting_name, default)
  1755. @classmethod
  1756. def load_settings(cls):
  1757. cls.all_settings = {item.key: item.value for item in cls.objects.all()}
  1758. cls.load_ready = True
  1759. @classmethod
  1760. def reload_settings(cls):
  1761. logger.debug("reload system settings.")
  1762. cls.load_ready = False
  1763. cls.load_settings()
  1764. @classmethod
  1765. def set_setting(cls, key, value):
  1766. cls.objects(key=key).update_one(upsert=True, **{"value": value})
  1767. cls.all_settings[key] = value
  1768. @classmethod
  1769. def set_mem_setting(cls, key, value):
  1770. cls.all_settings[key] = value
  1771. @classmethod
  1772. def get_support_redpack_list(cls):
  1773. obj = cls.objects.filter(key='SUPPORT_REDPACK_LIST').first()
  1774. if obj:
  1775. return json_loads(obj.value)
  1776. else:
  1777. return []
  1778. @classmethod
  1779. def set_support_redpack_list(cls, dataList):
  1780. value = json_dumps(dataList)
  1781. return cls.objects.filter(key='SUPPORT_REDPACK_LIST').update(value=value)
  1782. @classmethod
  1783. def disable_alipay_ruhui(cls):
  1784. item = cls.objects.filter(key='DISABLE_REDPACK').first()
  1785. if not item:
  1786. vaule = {}
  1787. else:
  1788. vaule = item.value
  1789. return vaule.get('RUHUI', False)
  1790. @classmethod
  1791. def disable_alipay_laxin(cls):
  1792. item = cls.objects.filter(key='DISABLE_REDPACK').first()
  1793. if not item:
  1794. vaule = {}
  1795. else:
  1796. vaule = item.value
  1797. return vaule.get('LAXIN', False)
  1798. @classmethod
  1799. def get_system_setting_direct(cls, setting_name, default = None):
  1800. obj = cls.objects.filter(key = setting_name).first()
  1801. if obj:
  1802. return obj.value
  1803. else:
  1804. return default
  1805. class DriverCode(Searchable):
  1806. code = StringField(verbose_name = u'驱动编码', unique = True)
  1807. name = StringField(verbose_name = u'驱动名称', unique = True)
  1808. description = StringField(verbose_name = u'驱动描述')
  1809. createdTime = DateTimeField(default = datetime.datetime.now, verbose_name = u'添加进来的时间')
  1810. adapterFile = StringField(verbose_name = '业务处理文件名称', default = '')
  1811. adapterVer = StringField(verbose_name = '业务版本', default = '2.0') # 发生变化后,会自动化加载
  1812. adapter = StringField(verbose_name = '适配器类名称', default = '')
  1813. eventerFile = StringField(verbose_name = '事件处理文件名称', default = '')
  1814. eventerVer = StringField(verbose_name = '事件处理版本', default = '2.0') # 发生变化后,会自动化加载
  1815. features = DictField(verbose_name = "额外特征", default = {}) # 属于某个设备类型的特征
  1816. meta = {'collection': 'device_driver_code', 'db_alias': 'default'}
  1817. def __repr__(self):
  1818. return '<DriverCode code=%s>' % (self.code,)
  1819. def to_dict(self):
  1820. return {
  1821. 'code': self.code,
  1822. 'name': self.name,
  1823. 'features': self.features,
  1824. 'description': self.description
  1825. }
  1826. @classmethod
  1827. def get_driver_code(cls, code):
  1828. # type: (str)->dict
  1829. cache_key = lambda code: 'driverCode{}'.format(code)
  1830. if not code: return None
  1831. driverInfo = cache.get(cache_key(code))
  1832. if driverInfo:
  1833. return driverInfo
  1834. else:
  1835. obj = cls.objects(code = code).first() # type: DriverCode
  1836. if obj:
  1837. value = {
  1838. 'id': str(obj.id),
  1839. 'code': obj.code,
  1840. 'name': obj.name,
  1841. 'description': obj.description,
  1842. 'createdTime': obj.to_datetime_str(obj.createdTime),
  1843. 'adapterFile': obj.adapterFile,
  1844. 'eventerFile': obj.eventerFile,
  1845. 'features': dict(obj.features)
  1846. }
  1847. cache.set(cache_key(code), value)
  1848. return value
  1849. return None
  1850. class DriverAdapter(DynamicDocument):
  1851. adapterFile = StringField(verbose_name = '业务处理文件名称', unique = True)
  1852. adapterVer = StringField(verbose_name = '业务版本', default = '1.0')
  1853. adapter = StringField(verbose_name = '适配器类名称', default = '')
  1854. meta = {'collection': 'device_driver_adapter', 'db_alias': 'default'}
  1855. _lock = ThreadLock()
  1856. adapters = {}
  1857. def to_dict(self):
  1858. return {
  1859. 'adapterFile': self.adapterFile,
  1860. 'adapterVer': self.adapterVer,
  1861. 'adapter': self.adapter
  1862. }
  1863. @classmethod
  1864. def get_driver_adapter(cls, code, dev, online = True):
  1865. cache_key = lambda filename: 'driverAdapter{}'.format(filename)
  1866. adapter_file = None
  1867. if not online:
  1868. adapter_file = 'offline'
  1869. else:
  1870. driver_code_info = DriverCode.get_driver_code(code)
  1871. if driver_code_info:
  1872. adapter_file = driver_code_info['adapterFile']
  1873. if not adapter_file:
  1874. adapter_file = 'commonPulse'
  1875. adapter_info = cache.get(cache_key(adapter_file))
  1876. if not adapter_info:
  1877. adapter = cls.objects.get(adapterFile = adapter_file)
  1878. adapter_info = {
  1879. 'adapterFile': adapter.adapterFile,
  1880. 'adapterVer': adapter.adapterVer,
  1881. 'adapter': adapter.adapter
  1882. }
  1883. cache.set(cache_key(adapter_file), {
  1884. 'adapterFile': adapter_info['adapterFile'],
  1885. 'adapterVer': adapter_info['adapterVer'],
  1886. 'adapter': adapter_info['adapter']
  1887. })
  1888. logger.debug('[cache] load driver adapter info is: {}'.format(adapter_info))
  1889. mem_info = cls.adapters.get(adapter_file, None)
  1890. if mem_info and 'module' in mem_info and \
  1891. mem_info['adapterVer'] == adapter_info['adapterVer'] and \
  1892. mem_info['adapterFile'] == adapter_info['adapterFile'] and \
  1893. mem_info['adapter'] == adapter_info['adapter']:
  1894. adapter_module = cls.adapters[adapter_file]['module']
  1895. logger.debug('adapter module {} has loaded.'.format(adapter_module))
  1896. else:
  1897. try:
  1898. cls._lock.acquire_lock()
  1899. mem_info = cls.adapters.get(adapter_file, None)
  1900. if mem_info and 'module' in mem_info and \
  1901. mem_info['adapterVer'] == adapter_info['adapterVer'] and \
  1902. mem_info['adapterFile'] == adapter_info['adapterFile'] and \
  1903. mem_info['adapter'] == adapter_info['adapter']:
  1904. adapter_module = cls.adapters[adapter_file]['module']
  1905. logger.debug('adapter module {} has loaded.'.format(adapter_module))
  1906. else:
  1907. if adapter_file in cls.adapters:
  1908. mem_info = cls.adapters[adapter_file]
  1909. logger.info('device adapter changed, need reload, oldInfo=%s, newInfo=%s' % (
  1910. mem_info, adapter_info))
  1911. module_name = 'apps.web.core.adapter.{}'.format(adapter_info['adapterFile'])
  1912. adapter_module = import_module(module_name)
  1913. logger.info('finished import new module = {}'.format(adapter_module))
  1914. adapter_module = eval('reload({})'.format(module_name))
  1915. logger.info('finished reload new module = {}'.format(adapter_module))
  1916. adapter_info.update({'module': adapter_module})
  1917. cls.adapters[adapter_file] = adapter_info
  1918. else:
  1919. logger.info('loading device adapter, adapter info = {}'.format(adapter_info))
  1920. module_name = 'apps.web.core.adapter.{}'.format(adapter_file)
  1921. adapter_module = import_module(module_name)
  1922. logger.info('finished import new module = {}'.format(adapter_module))
  1923. adapter_info.update({'module': adapter_module})
  1924. cls.adapters[adapter_file] = adapter_info
  1925. finally:
  1926. cls._lock.release_lock()
  1927. return eval('adapter_module.%s(dev)' % (adapter_info['adapter']))
  1928. class DriverEventer(DynamicDocument):
  1929. eventerFile = StringField(verbose_name = '事件处理文件名称', unique = True)
  1930. eventerVer = StringField(verbose_name = '事件处理版本', default = '1.0') # 发生变化后,会自动化加载
  1931. meta = {'collection': 'device_driver_eventer', 'db_alias': 'default'}
  1932. _lock = ThreadLock()
  1933. eventers = {}
  1934. def to_dict(self):
  1935. return {
  1936. 'id': str(self.id),
  1937. 'eventerFile': self.eventerFile,
  1938. 'eventerVer': self.eventerVer
  1939. }
  1940. @classmethod
  1941. def get_driver_eventer(cls, code, device_adapter, online = True):
  1942. cache_key = lambda filename: 'driverEventer{}'.format(filename)
  1943. eventer_file = None
  1944. if not online:
  1945. eventer_file = 'offline'
  1946. else:
  1947. driver_code_info = DriverCode.get_driver_code(code)
  1948. if driver_code_info:
  1949. eventer_file = driver_code_info['eventerFile']
  1950. if not eventer_file:
  1951. if device_adapter.__class__.__name__ == 'CommonPulseAdapter':
  1952. eventer_file = 'commonPulse'
  1953. else:
  1954. eventer_file = 'dummy'
  1955. eventer_info = cache.get(cache_key(eventer_file))
  1956. if not eventer_info:
  1957. eventer = cls.objects.get(eventerFile = eventer_file)
  1958. eventer_info = {'eventerFile': eventer.eventerFile, 'eventerVer': eventer.eventerVer}
  1959. logger.debug('load driver eventer info is: {}'.format(eventer_info))
  1960. cache.set(cache_key(eventer_file),
  1961. {
  1962. 'eventerFile': eventer_info['eventerFile'],
  1963. 'eventerVer': eventer_info['eventerVer']
  1964. })
  1965. mem_info = cls.eventers.get(eventer_file, None)
  1966. if mem_info and 'module' in mem_info and mem_info['eventerVer'] == eventer_info['eventerVer'] and \
  1967. mem_info['eventerFile'] == eventer_info['eventerFile']:
  1968. eventer_module = mem_info['module']
  1969. logger.debug('eventer module {} has loaded.'.format(eventer_module))
  1970. else:
  1971. try:
  1972. cls._lock.acquire_lock()
  1973. mem_info = cls.eventers.get(eventer_file, None)
  1974. if mem_info and 'module' in mem_info and mem_info['eventerVer'] == eventer_info['eventerVer'] and \
  1975. mem_info['eventerFile'] == eventer_info['eventerFile']:
  1976. eventer_module = mem_info['module']
  1977. logger.debug('eventer module {} has loaded.'.format(eventer_module))
  1978. else:
  1979. if mem_info:
  1980. logger.info(
  1981. 'device eventer changed, need reload, oldInfo = %s, newInfo = %s' % (
  1982. mem_info, eventer_info))
  1983. module_name = 'apps.web.eventer.{}'.format(eventer_file)
  1984. eventer_module = import_module(module_name)
  1985. logger.info('finished import new eventer module = {}'.format(eventer_module))
  1986. eventer_module = eval('reload({})'.format(module_name))
  1987. logger.info('finished reload new eventer module = {}'.format(eventer_module))
  1988. eventer_info.update({'module': eventer_module})
  1989. cls.eventers[eventer_file] = eventer_info
  1990. else:
  1991. logger.info('loading device eventer, eventer info = {}'.format(eventer_info))
  1992. module_name = 'apps.web.eventer.{}'.format(eventer_file)
  1993. eventer_module = import_module(module_name)
  1994. logger.info('finished import new eventer module = {}'.format(eventer_module))
  1995. eventer_info.update({'module': eventer_module})
  1996. cls.eventers[eventer_file] = eventer_info
  1997. finally:
  1998. cls._lock.release_lock()
  1999. return eval('eventer_module.builder(device_adapter)')
  2000. class AliUserAgreementSign(Searchable):
  2001. """
  2002. 阿里用户签约
  2003. """
  2004. class SignStatus(IterConstant):
  2005. INIT = 'INIT'
  2006. TEMP = 'TEMP'
  2007. NORMAL = 'NORMAL'
  2008. STOP = 'STOP'
  2009. externalAgreementNo = StringField(verbose_name = u"商户传入的协议编号")
  2010. agreementNo = StringField(verbose_name = u"签约号")
  2011. personalProductCode = StringField(verbose_name = u'个人产品码')
  2012. signScene = StringField(verbose_name = u'场景码')
  2013. accessParams = DictField(verbose_name = u'端类型')
  2014. productCode = StringField(verbose_name = u"商家签约的产品码")
  2015. thirdPartyType = StringField(verbose_name = u"商家类型")
  2016. externalLogonId = StringField(verbose_name = u"用户在商户网站的登录账号,用于在签约页面展示,如果为空,则不展示")
  2017. dateTimeAdded = DateTimeField(verbose_name = u"创建时间", default = datetime.datetime.now)
  2018. signTime = DateTimeField(verbose_name = u"创建时间")
  2019. attachParas = DictField(verbose_name = u'签约完成后操作所需要的参数')
  2020. cooperAppId = StringField(verbose_name = u'合作单位APPID')
  2021. meta = {"collection": "ali_user_agreement_sign", "db_alias": "default"}
  2022. @classmethod
  2023. def issue(cls, cooperAppId, external_agreement_no, external_logon_id, attach):
  2024. return cls(
  2025. cooperAppId = cooperAppId,
  2026. externalAgreementNo = external_agreement_no,
  2027. personalProductCode = 'FUND_SAFT_SIGN_WITHHOLDING_P',
  2028. signScene = 'INDUSTRY|SATF_ACC',
  2029. accessParams = {"channel": "QRCODE"},
  2030. productCode = 'FUND_SAFT_SIGN_WITHHOLDING',
  2031. thirdPartyType = 'PARTNER',
  2032. externalLogonId = external_logon_id,
  2033. attachParas = attach,
  2034. dateTimeAdded = datetime.datetime.now()).save()
  2035. @classmethod
  2036. def makeNo(cls, external_logon_id):
  2037. identifier = hashlib.md5(external_logon_id.encode()).hexdigest().upper()
  2038. return OrderNoMaker.make_order_no_32(
  2039. identifier, OrderMainType.OTHER, OtherOrderSubType.ALI_USER_AGREEMENT_SIG)